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 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                        // filter out meta files as they are not considered assets
143                        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}