bevy_asset/io/file/
sync_file_asset.rs

1use futures_io::{AsyncRead, AsyncSeek, AsyncWrite};
2use futures_lite::Stream;
3
4use crate::io::{
5    get_meta_path, AssetReader, AssetReaderError, AssetWriter, AssetWriterError, PathStream,
6    Reader, Writer,
7};
8
9use std::{
10    fs::{read_dir, File},
11    io::{Read, Seek, Write},
12    path::{Path, PathBuf},
13    pin::Pin,
14    task::Poll,
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 std::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 AsyncSeek for FileReader {
34    fn poll_seek(
35        self: Pin<&mut Self>,
36        _cx: &mut std::task::Context<'_>,
37        pos: std::io::SeekFrom,
38    ) -> Poll<std::io::Result<u64>> {
39        let this = self.get_mut();
40        let seek = this.0.seek(pos);
41        Poll::Ready(seek)
42    }
43}
44
45struct FileWriter(File);
46
47impl AsyncWrite for FileWriter {
48    fn poll_write(
49        self: Pin<&mut Self>,
50        _cx: &mut std::task::Context<'_>,
51        buf: &[u8],
52    ) -> Poll<std::io::Result<usize>> {
53        let this = self.get_mut();
54        let wrote = this.0.write(buf);
55        Poll::Ready(wrote)
56    }
57
58    fn poll_flush(
59        self: Pin<&mut Self>,
60        _cx: &mut std::task::Context<'_>,
61    ) -> Poll<std::io::Result<()>> {
62        let this = self.get_mut();
63        let flushed = this.0.flush();
64        Poll::Ready(flushed)
65    }
66
67    fn poll_close(
68        self: Pin<&mut Self>,
69        _cx: &mut std::task::Context<'_>,
70    ) -> Poll<std::io::Result<()>> {
71        Poll::Ready(Ok(()))
72    }
73}
74
75struct DirReader(Vec<PathBuf>);
76
77impl Stream for DirReader {
78    type Item = PathBuf;
79
80    fn poll_next(
81        self: Pin<&mut Self>,
82        _cx: &mut std::task::Context<'_>,
83    ) -> Poll<Option<Self::Item>> {
84        let this = self.get_mut();
85        Poll::Ready(this.0.pop())
86    }
87}
88
89impl AssetReader for FileAssetReader {
90    async fn read<'a>(&'a self, path: &'a Path) -> Result<Box<Reader<'a>>, AssetReaderError> {
91        let full_path = self.root_path.join(path);
92        match File::open(&full_path) {
93            Ok(file) => {
94                let reader: Box<Reader> = Box::new(FileReader(file));
95                Ok(reader)
96            }
97            Err(e) => {
98                if e.kind() == std::io::ErrorKind::NotFound {
99                    Err(AssetReaderError::NotFound(full_path))
100                } else {
101                    Err(e.into())
102                }
103            }
104        }
105    }
106
107    async fn read_meta<'a>(&'a self, path: &'a Path) -> Result<Box<Reader<'a>>, AssetReaderError> {
108        let meta_path = get_meta_path(path);
109        let full_path = self.root_path.join(meta_path);
110        match File::open(&full_path) {
111            Ok(file) => {
112                let reader: Box<Reader> = Box::new(FileReader(file));
113                Ok(reader)
114            }
115            Err(e) => {
116                if e.kind() == std::io::ErrorKind::NotFound {
117                    Err(AssetReaderError::NotFound(full_path))
118                } else {
119                    Err(e.into())
120                }
121            }
122        }
123    }
124
125    async fn read_directory<'a>(
126        &'a self,
127        path: &'a Path,
128    ) -> Result<Box<PathStream>, AssetReaderError> {
129        let full_path = self.root_path.join(path);
130        match read_dir(&full_path) {
131            Ok(read_dir) => {
132                let root_path = self.root_path.clone();
133                let mapped_stream = read_dir.filter_map(move |f| {
134                    f.ok().and_then(|dir_entry| {
135                        let path = dir_entry.path();
136                        // filter out meta files as they are not considered assets
137                        if let Some(ext) = path.extension().and_then(|e| e.to_str()) {
138                            if ext.eq_ignore_ascii_case("meta") {
139                                return None;
140                            }
141                        }
142                        let relative_path = path.strip_prefix(&root_path).unwrap();
143                        Some(relative_path.to_owned())
144                    })
145                });
146                let read_dir: Box<PathStream> = Box::new(DirReader(mapped_stream.collect()));
147                Ok(read_dir)
148            }
149            Err(e) => {
150                if e.kind() == std::io::ErrorKind::NotFound {
151                    Err(AssetReaderError::NotFound(full_path))
152                } else {
153                    Err(e.into())
154                }
155            }
156        }
157    }
158
159    async fn is_directory<'a>(
160        &'a self,
161        path: &'a Path,
162    ) -> std::result::Result<bool, AssetReaderError> {
163        let full_path = self.root_path.join(path);
164        let metadata = full_path
165            .metadata()
166            .map_err(|_e| AssetReaderError::NotFound(path.to_owned()))?;
167        Ok(metadata.file_type().is_dir())
168    }
169}
170
171impl AssetWriter for FileAssetWriter {
172    async fn write<'a>(&'a self, path: &'a Path) -> Result<Box<Writer>, AssetWriterError> {
173        let full_path = self.root_path.join(path);
174        if let Some(parent) = full_path.parent() {
175            std::fs::create_dir_all(parent)?;
176        }
177        let file = File::create(&full_path)?;
178        let writer: Box<Writer> = Box::new(FileWriter(file));
179        Ok(writer)
180    }
181
182    async fn write_meta<'a>(&'a self, path: &'a Path) -> Result<Box<Writer>, AssetWriterError> {
183        let meta_path = get_meta_path(path);
184        let full_path = self.root_path.join(meta_path);
185        if let Some(parent) = full_path.parent() {
186            std::fs::create_dir_all(parent)?;
187        }
188        let file = File::create(&full_path)?;
189        let writer: Box<Writer> = Box::new(FileWriter(file));
190        Ok(writer)
191    }
192
193    async fn remove<'a>(&'a self, path: &'a Path) -> std::result::Result<(), AssetWriterError> {
194        let full_path = self.root_path.join(path);
195        std::fs::remove_file(full_path)?;
196        Ok(())
197    }
198
199    async fn remove_meta<'a>(
200        &'a self,
201        path: &'a Path,
202    ) -> std::result::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 remove_directory<'a>(
210        &'a self,
211        path: &'a Path,
212    ) -> std::result::Result<(), AssetWriterError> {
213        let full_path = self.root_path.join(path);
214        std::fs::remove_dir_all(full_path)?;
215        Ok(())
216    }
217
218    async fn remove_empty_directory<'a>(
219        &'a self,
220        path: &'a Path,
221    ) -> std::result::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    ) -> std::result::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    ) -> std::result::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    ) -> std::result::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}