Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_asset/src/io/file/sync_file_asset.rs
6601 views
1
use futures_io::{AsyncRead, AsyncWrite};
2
use futures_lite::Stream;
3
4
use crate::io::{
5
get_meta_path, AssetReader, AssetReaderError, AssetWriter, AssetWriterError, AsyncSeekForward,
6
PathStream, Reader, Writer,
7
};
8
9
use alloc::{borrow::ToOwned, boxed::Box, vec::Vec};
10
use core::{pin::Pin, task::Poll};
11
use std::{
12
fs::{read_dir, File},
13
io::{Read, Seek, Write},
14
path::{Path, PathBuf},
15
};
16
17
use super::{FileAssetReader, FileAssetWriter};
18
19
struct FileReader(File);
20
21
impl 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
33
impl 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
47
impl 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
57
struct FileWriter(File);
58
59
impl 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
87
struct DirReader(Vec<PathBuf>);
88
89
impl 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
101
impl 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
&& ext.eq_ignore_ascii_case("meta")
145
{
146
return None;
147
}
148
// filter out hidden files. they are not listed by default but are directly targetable
149
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
184
impl 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
}
277
278