bevy_asset/io/file/
sync_file_asset.rs1use futures_io::{AsyncRead, AsyncWrite};
2use futures_lite::Stream;
3
4use crate::io::{
5 get_meta_path, AssetReader, AssetReaderError, AssetWriter, AssetWriterError, AsyncSeekForward,
6 PathStream, Reader, Writer,
7};
8
9use alloc::{borrow::ToOwned, boxed::Box, vec::Vec};
10use core::{pin::Pin, task::Poll};
11use std::{
12 fs::{read_dir, File},
13 io::{Read, Seek, Write},
14 path::{Path, PathBuf},
15};
16
17use super::{FileAssetReader, FileAssetWriter};
18
19struct FileReader(File);
20
21impl AsyncRead for FileReader {
22 fn poll_read(
23 self: Pin<&mut Self>,
24 _cx: &mut core::task::Context<'_>,
25 buf: &mut [u8],
26 ) -> Poll<std::io::Result<usize>> {
27 let this = self.get_mut();
28 let read = this.0.read(buf);
29 Poll::Ready(read)
30 }
31}
32
33impl AsyncSeekForward for FileReader {
34 fn poll_seek_forward(
35 self: Pin<&mut Self>,
36 _cx: &mut core::task::Context<'_>,
37 offset: u64,
38 ) -> Poll<std::io::Result<u64>> {
39 let this = self.get_mut();
40 let current = this.0.stream_position()?;
41 let seek = this.0.seek(std::io::SeekFrom::Start(current + offset));
42
43 Poll::Ready(seek)
44 }
45}
46
47impl Reader for FileReader {
48 fn read_to_end<'a>(
49 &'a mut self,
50 buf: &'a mut Vec<u8>,
51 ) -> stackfuture::StackFuture<'a, std::io::Result<usize>, { crate::io::STACK_FUTURE_SIZE }>
52 {
53 stackfuture::StackFuture::from(async { self.0.read_to_end(buf) })
54 }
55}
56
57struct FileWriter(File);
58
59impl AsyncWrite for FileWriter {
60 fn poll_write(
61 self: Pin<&mut Self>,
62 _cx: &mut core::task::Context<'_>,
63 buf: &[u8],
64 ) -> Poll<std::io::Result<usize>> {
65 let this = self.get_mut();
66 let wrote = this.0.write(buf);
67 Poll::Ready(wrote)
68 }
69
70 fn poll_flush(
71 self: Pin<&mut Self>,
72 _cx: &mut core::task::Context<'_>,
73 ) -> Poll<std::io::Result<()>> {
74 let this = self.get_mut();
75 let flushed = this.0.flush();
76 Poll::Ready(flushed)
77 }
78
79 fn poll_close(
80 self: Pin<&mut Self>,
81 _cx: &mut core::task::Context<'_>,
82 ) -> Poll<std::io::Result<()>> {
83 Poll::Ready(Ok(()))
84 }
85}
86
87struct DirReader(Vec<PathBuf>);
88
89impl Stream for DirReader {
90 type Item = PathBuf;
91
92 fn poll_next(
93 self: Pin<&mut Self>,
94 _cx: &mut core::task::Context<'_>,
95 ) -> Poll<Option<Self::Item>> {
96 let this = self.get_mut();
97 Poll::Ready(this.0.pop())
98 }
99}
100
101impl AssetReader for FileAssetReader {
102 async fn read<'a>(&'a self, path: &'a Path) -> Result<impl Reader + 'a, AssetReaderError> {
103 let full_path = self.root_path.join(path);
104 match File::open(&full_path) {
105 Ok(file) => Ok(FileReader(file)),
106 Err(e) => {
107 if e.kind() == std::io::ErrorKind::NotFound {
108 Err(AssetReaderError::NotFound(full_path))
109 } else {
110 Err(e.into())
111 }
112 }
113 }
114 }
115
116 async fn read_meta<'a>(&'a self, path: &'a Path) -> Result<impl Reader + 'a, AssetReaderError> {
117 let meta_path = get_meta_path(path);
118 let full_path = self.root_path.join(meta_path);
119 match File::open(&full_path) {
120 Ok(file) => Ok(FileReader(file)),
121 Err(e) => {
122 if e.kind() == std::io::ErrorKind::NotFound {
123 Err(AssetReaderError::NotFound(full_path))
124 } else {
125 Err(e.into())
126 }
127 }
128 }
129 }
130
131 async fn read_directory<'a>(
132 &'a self,
133 path: &'a Path,
134 ) -> Result<Box<PathStream>, AssetReaderError> {
135 let full_path = self.root_path.join(path);
136 match read_dir(&full_path) {
137 Ok(read_dir) => {
138 let root_path = self.root_path.clone();
139 let mapped_stream = read_dir.filter_map(move |f| {
140 f.ok().and_then(|dir_entry| {
141 let path = dir_entry.path();
142 if let Some(ext) = path.extension().and_then(|e| e.to_str()) {
144 if ext.eq_ignore_ascii_case("meta") {
145 return None;
146 }
147 }
148 let relative_path = path.strip_prefix(&root_path).unwrap();
149 Some(relative_path.to_owned())
150 })
151 });
152 let read_dir: Box<PathStream> = Box::new(DirReader(mapped_stream.collect()));
153 Ok(read_dir)
154 }
155 Err(e) => {
156 if e.kind() == std::io::ErrorKind::NotFound {
157 Err(AssetReaderError::NotFound(full_path))
158 } else {
159 Err(e.into())
160 }
161 }
162 }
163 }
164
165 async fn is_directory<'a>(&'a self, path: &'a Path) -> Result<bool, AssetReaderError> {
166 let full_path = self.root_path.join(path);
167 let metadata = full_path
168 .metadata()
169 .map_err(|_e| AssetReaderError::NotFound(path.to_owned()))?;
170 Ok(metadata.file_type().is_dir())
171 }
172}
173
174impl AssetWriter for FileAssetWriter {
175 async fn write<'a>(&'a self, path: &'a Path) -> Result<Box<Writer>, AssetWriterError> {
176 let full_path = self.root_path.join(path);
177 if let Some(parent) = full_path.parent() {
178 std::fs::create_dir_all(parent)?;
179 }
180 let file = File::create(&full_path)?;
181 let writer: Box<Writer> = Box::new(FileWriter(file));
182 Ok(writer)
183 }
184
185 async fn write_meta<'a>(&'a self, path: &'a Path) -> Result<Box<Writer>, AssetWriterError> {
186 let meta_path = get_meta_path(path);
187 let full_path = self.root_path.join(meta_path);
188 if let Some(parent) = full_path.parent() {
189 std::fs::create_dir_all(parent)?;
190 }
191 let file = File::create(&full_path)?;
192 let writer: Box<Writer> = Box::new(FileWriter(file));
193 Ok(writer)
194 }
195
196 async fn remove<'a>(&'a self, path: &'a Path) -> Result<(), AssetWriterError> {
197 let full_path = self.root_path.join(path);
198 std::fs::remove_file(full_path)?;
199 Ok(())
200 }
201
202 async fn remove_meta<'a>(&'a self, path: &'a Path) -> Result<(), AssetWriterError> {
203 let meta_path = get_meta_path(path);
204 let full_path = self.root_path.join(meta_path);
205 std::fs::remove_file(full_path)?;
206 Ok(())
207 }
208
209 async fn create_directory<'a>(&'a self, path: &'a Path) -> Result<(), AssetWriterError> {
210 let full_path = self.root_path.join(path);
211 std::fs::create_dir_all(full_path)?;
212 Ok(())
213 }
214
215 async fn remove_directory<'a>(&'a self, path: &'a Path) -> Result<(), AssetWriterError> {
216 let full_path = self.root_path.join(path);
217 std::fs::remove_dir_all(full_path)?;
218 Ok(())
219 }
220
221 async fn remove_empty_directory<'a>(&'a self, path: &'a Path) -> Result<(), AssetWriterError> {
222 let full_path = self.root_path.join(path);
223 std::fs::remove_dir(full_path)?;
224 Ok(())
225 }
226
227 async fn remove_assets_in_directory<'a>(
228 &'a self,
229 path: &'a Path,
230 ) -> Result<(), AssetWriterError> {
231 let full_path = self.root_path.join(path);
232 std::fs::remove_dir_all(&full_path)?;
233 std::fs::create_dir_all(&full_path)?;
234 Ok(())
235 }
236
237 async fn rename<'a>(
238 &'a self,
239 old_path: &'a Path,
240 new_path: &'a Path,
241 ) -> Result<(), AssetWriterError> {
242 let full_old_path = self.root_path.join(old_path);
243 let full_new_path = self.root_path.join(new_path);
244 if let Some(parent) = full_new_path.parent() {
245 std::fs::create_dir_all(parent)?;
246 }
247 std::fs::rename(full_old_path, full_new_path)?;
248 Ok(())
249 }
250
251 async fn rename_meta<'a>(
252 &'a self,
253 old_path: &'a Path,
254 new_path: &'a Path,
255 ) -> Result<(), AssetWriterError> {
256 let old_meta_path = get_meta_path(old_path);
257 let new_meta_path = get_meta_path(new_path);
258 let full_old_path = self.root_path.join(old_meta_path);
259 let full_new_path = self.root_path.join(new_meta_path);
260 if let Some(parent) = full_new_path.parent() {
261 std::fs::create_dir_all(parent)?;
262 }
263 std::fs::rename(full_old_path, full_new_path)?;
264 Ok(())
265 }
266}