bevy_asset/io/file/
file_asset.rs1use crate::io::{
2 get_meta_path, AssetReader, AssetReaderError, AssetWriter, AssetWriterError, AsyncSeekForward,
3 PathStream, Reader, Writer,
4};
5use async_fs::{read_dir, File};
6use futures_io::AsyncSeek;
7use futures_lite::StreamExt;
8
9use alloc::{borrow::ToOwned, boxed::Box};
10use core::{pin::Pin, task, task::Poll};
11use std::path::Path;
12
13use super::{FileAssetReader, FileAssetWriter};
14
15impl AsyncSeekForward for File {
16 fn poll_seek_forward(
17 mut self: Pin<&mut Self>,
18 cx: &mut task::Context<'_>,
19 offset: u64,
20 ) -> Poll<futures_io::Result<u64>> {
21 let offset: Result<i64, _> = offset.try_into();
22
23 if let Ok(offset) = offset {
24 Pin::new(&mut self).poll_seek(cx, futures_io::SeekFrom::Current(offset))
25 } else {
26 Poll::Ready(Err(std::io::Error::new(
27 std::io::ErrorKind::InvalidInput,
28 "seek position is out of range",
29 )))
30 }
31 }
32}
33
34impl Reader for File {}
35
36impl AssetReader for FileAssetReader {
37 async fn read<'a>(&'a self, path: &'a Path) -> Result<impl Reader + 'a, AssetReaderError> {
38 let full_path = self.root_path.join(path);
39 File::open(&full_path).await.map_err(|e| {
40 if e.kind() == std::io::ErrorKind::NotFound {
41 AssetReaderError::NotFound(full_path)
42 } else {
43 e.into()
44 }
45 })
46 }
47
48 async fn read_meta<'a>(&'a self, path: &'a Path) -> Result<impl Reader + 'a, AssetReaderError> {
49 let meta_path = get_meta_path(path);
50 let full_path = self.root_path.join(meta_path);
51 File::open(&full_path).await.map_err(|e| {
52 if e.kind() == std::io::ErrorKind::NotFound {
53 AssetReaderError::NotFound(full_path)
54 } else {
55 e.into()
56 }
57 })
58 }
59
60 async fn read_directory<'a>(
61 &'a self,
62 path: &'a Path,
63 ) -> Result<Box<PathStream>, AssetReaderError> {
64 let full_path = self.root_path.join(path);
65 match read_dir(&full_path).await {
66 Ok(read_dir) => {
67 let root_path = self.root_path.clone();
68 let mapped_stream = read_dir.filter_map(move |f| {
69 f.ok().and_then(|dir_entry| {
70 let path = dir_entry.path();
71 if let Some(ext) = path.extension().and_then(|e| e.to_str())
73 && ext.eq_ignore_ascii_case("meta")
74 {
75 return None;
76 }
77 if path
79 .file_name()
80 .and_then(|file_name| file_name.to_str())
81 .map(|file_name| file_name.starts_with('.'))
82 .unwrap_or_default()
83 {
84 return None;
85 }
86 let relative_path = path.strip_prefix(&root_path).unwrap();
87 Some(relative_path.to_owned())
88 })
89 });
90 let read_dir: Box<PathStream> = Box::new(mapped_stream);
91 Ok(read_dir)
92 }
93 Err(e) => {
94 if e.kind() == std::io::ErrorKind::NotFound {
95 Err(AssetReaderError::NotFound(full_path))
96 } else {
97 Err(e.into())
98 }
99 }
100 }
101 }
102
103 async fn is_directory<'a>(&'a self, path: &'a Path) -> Result<bool, AssetReaderError> {
104 let full_path = self.root_path.join(path);
105 let metadata = full_path
106 .metadata()
107 .map_err(|_e| AssetReaderError::NotFound(path.to_owned()))?;
108 Ok(metadata.file_type().is_dir())
109 }
110}
111
112impl AssetWriter for FileAssetWriter {
113 async fn write<'a>(&'a self, path: &'a Path) -> Result<Box<Writer>, AssetWriterError> {
114 let full_path = self.root_path.join(path);
115 if let Some(parent) = full_path.parent() {
116 async_fs::create_dir_all(parent).await?;
117 }
118 let file = File::create(&full_path).await?;
119 let writer: Box<Writer> = Box::new(file);
120 Ok(writer)
121 }
122
123 async fn write_meta<'a>(&'a self, path: &'a Path) -> Result<Box<Writer>, AssetWriterError> {
124 let meta_path = get_meta_path(path);
125 let full_path = self.root_path.join(meta_path);
126 if let Some(parent) = full_path.parent() {
127 async_fs::create_dir_all(parent).await?;
128 }
129 let file = File::create(&full_path).await?;
130 let writer: Box<Writer> = Box::new(file);
131 Ok(writer)
132 }
133
134 async fn remove<'a>(&'a self, path: &'a Path) -> Result<(), AssetWriterError> {
135 let full_path = self.root_path.join(path);
136 async_fs::remove_file(full_path).await?;
137 Ok(())
138 }
139
140 async fn remove_meta<'a>(&'a self, path: &'a Path) -> Result<(), AssetWriterError> {
141 let meta_path = get_meta_path(path);
142 let full_path = self.root_path.join(meta_path);
143 async_fs::remove_file(full_path).await?;
144 Ok(())
145 }
146
147 async fn rename<'a>(
148 &'a self,
149 old_path: &'a Path,
150 new_path: &'a Path,
151 ) -> Result<(), AssetWriterError> {
152 let full_old_path = self.root_path.join(old_path);
153 let full_new_path = self.root_path.join(new_path);
154 if let Some(parent) = full_new_path.parent() {
155 async_fs::create_dir_all(parent).await?;
156 }
157 async_fs::rename(full_old_path, full_new_path).await?;
158 Ok(())
159 }
160
161 async fn rename_meta<'a>(
162 &'a self,
163 old_path: &'a Path,
164 new_path: &'a Path,
165 ) -> Result<(), AssetWriterError> {
166 let old_meta_path = get_meta_path(old_path);
167 let new_meta_path = get_meta_path(new_path);
168 let full_old_path = self.root_path.join(old_meta_path);
169 let full_new_path = self.root_path.join(new_meta_path);
170 if let Some(parent) = full_new_path.parent() {
171 async_fs::create_dir_all(parent).await?;
172 }
173 async_fs::rename(full_old_path, full_new_path).await?;
174 Ok(())
175 }
176
177 async fn create_directory<'a>(&'a self, path: &'a Path) -> Result<(), AssetWriterError> {
178 let full_path = self.root_path.join(path);
179 async_fs::create_dir_all(full_path).await?;
180 Ok(())
181 }
182
183 async fn remove_directory<'a>(&'a self, path: &'a Path) -> Result<(), AssetWriterError> {
184 let full_path = self.root_path.join(path);
185 async_fs::remove_dir_all(full_path).await?;
186 Ok(())
187 }
188
189 async fn remove_empty_directory<'a>(&'a self, path: &'a Path) -> Result<(), AssetWriterError> {
190 let full_path = self.root_path.join(path);
191 async_fs::remove_dir(full_path).await?;
192 Ok(())
193 }
194
195 async fn remove_assets_in_directory<'a>(
196 &'a self,
197 path: &'a Path,
198 ) -> Result<(), AssetWriterError> {
199 let full_path = self.root_path.join(path);
200 async_fs::remove_dir_all(&full_path).await?;
201 async_fs::create_dir_all(&full_path).await?;
202 Ok(())
203 }
204}