bevy_asset/io/file/
sync_file_asset.rs

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