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 && ext.eq_ignore_ascii_case("meta")
145 {
146 return None;
147 }
148 if path
150 .file_name()
151 .and_then(|file_name| file_name.to_str())
152 .map(|file_name| file_name.starts_with('.'))
153 .unwrap_or_default()
154 {
155 return None;
156 }
157
158 let relative_path = path.strip_prefix(&root_path).unwrap();
159 Some(relative_path.to_owned())
160 })
161 });
162 let read_dir: Box<PathStream> = Box::new(DirReader(mapped_stream.collect()));
163 Ok(read_dir)
164 }
165 Err(e) => {
166 if e.kind() == std::io::ErrorKind::NotFound {
167 Err(AssetReaderError::NotFound(full_path))
168 } else {
169 Err(e.into())
170 }
171 }
172 }
173 }
174
175 async fn is_directory<'a>(&'a self, path: &'a Path) -> Result<bool, AssetReaderError> {
176 let full_path = self.root_path.join(path);
177 let metadata = full_path
178 .metadata()
179 .map_err(|_e| AssetReaderError::NotFound(path.to_owned()))?;
180 Ok(metadata.file_type().is_dir())
181 }
182}
183
184impl AssetWriter for FileAssetWriter {
185 async fn write<'a>(&'a self, path: &'a Path) -> Result<Box<Writer>, AssetWriterError> {
186 let full_path = self.root_path.join(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 write_meta<'a>(&'a self, path: &'a Path) -> Result<Box<Writer>, AssetWriterError> {
196 let meta_path = get_meta_path(path);
197 let full_path = self.root_path.join(meta_path);
198 if let Some(parent) = full_path.parent() {
199 std::fs::create_dir_all(parent)?;
200 }
201 let file = File::create(&full_path)?;
202 let writer: Box<Writer> = Box::new(FileWriter(file));
203 Ok(writer)
204 }
205
206 async fn remove<'a>(&'a self, path: &'a Path) -> Result<(), AssetWriterError> {
207 let full_path = self.root_path.join(path);
208 std::fs::remove_file(full_path)?;
209 Ok(())
210 }
211
212 async fn remove_meta<'a>(&'a self, path: &'a Path) -> Result<(), AssetWriterError> {
213 let meta_path = get_meta_path(path);
214 let full_path = self.root_path.join(meta_path);
215 std::fs::remove_file(full_path)?;
216 Ok(())
217 }
218
219 async fn create_directory<'a>(&'a self, path: &'a Path) -> Result<(), AssetWriterError> {
220 let full_path = self.root_path.join(path);
221 std::fs::create_dir_all(full_path)?;
222 Ok(())
223 }
224
225 async fn remove_directory<'a>(&'a self, path: &'a Path) -> Result<(), AssetWriterError> {
226 let full_path = self.root_path.join(path);
227 std::fs::remove_dir_all(full_path)?;
228 Ok(())
229 }
230
231 async fn remove_empty_directory<'a>(&'a self, path: &'a Path) -> Result<(), AssetWriterError> {
232 let full_path = self.root_path.join(path);
233 std::fs::remove_dir(full_path)?;
234 Ok(())
235 }
236
237 async fn remove_assets_in_directory<'a>(
238 &'a self,
239 path: &'a Path,
240 ) -> Result<(), AssetWriterError> {
241 let full_path = self.root_path.join(path);
242 std::fs::remove_dir_all(&full_path)?;
243 std::fs::create_dir_all(&full_path)?;
244 Ok(())
245 }
246
247 async fn rename<'a>(
248 &'a self,
249 old_path: &'a Path,
250 new_path: &'a Path,
251 ) -> Result<(), AssetWriterError> {
252 let full_old_path = self.root_path.join(old_path);
253 let full_new_path = self.root_path.join(new_path);
254 if let Some(parent) = full_new_path.parent() {
255 std::fs::create_dir_all(parent)?;
256 }
257 std::fs::rename(full_old_path, full_new_path)?;
258 Ok(())
259 }
260
261 async fn rename_meta<'a>(
262 &'a self,
263 old_path: &'a Path,
264 new_path: &'a Path,
265 ) -> Result<(), AssetWriterError> {
266 let old_meta_path = get_meta_path(old_path);
267 let new_meta_path = get_meta_path(new_path);
268 let full_old_path = self.root_path.join(old_meta_path);
269 let full_new_path = self.root_path.join(new_meta_path);
270 if let Some(parent) = full_new_path.parent() {
271 std::fs::create_dir_all(parent)?;
272 }
273 std::fs::rename(full_old_path, full_new_path)?;
274 Ok(())
275 }
276}