Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_asset/src/io/file/file_asset.rs
9492 views
1
use crate::io::{
2
get_meta_path, AssetReader, AssetReaderError, AssetWriter, AssetWriterError, PathStream,
3
Reader, ReaderNotSeekableError, SeekableReader, Writer,
4
};
5
use async_fs::{read_dir, File};
6
#[cfg(not(target_os = "windows"))]
7
use async_io::Timer;
8
#[cfg(not(target_os = "windows"))]
9
use async_lock::{Semaphore, SemaphoreGuard};
10
use futures_lite::StreamExt;
11
12
use alloc::{borrow::ToOwned, boxed::Box};
13
#[cfg(target_os = "windows")]
14
use core::marker::PhantomData;
15
#[cfg(not(target_os = "windows"))]
16
use core::time::Duration;
17
#[cfg(not(target_os = "windows"))]
18
use futures_util::{future, pin_mut};
19
use std::path::Path;
20
21
use super::{FileAssetReader, FileAssetWriter};
22
23
impl Reader for File {
24
fn seekable(&mut self) -> Result<&mut dyn SeekableReader, ReaderNotSeekableError> {
25
Ok(self)
26
}
27
}
28
29
// Set to OS default limit / 2
30
// macos & ios: 256
31
// linux & android: 1024
32
#[cfg(any(target_os = "macos", target_os = "ios"))]
33
static OPEN_FILE_LIMITER: Semaphore = Semaphore::new(128);
34
#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "windows")))]
35
static OPEN_FILE_LIMITER: Semaphore = Semaphore::new(512);
36
37
#[cfg(not(target_os = "windows"))]
38
async fn maybe_get_semaphore<'a>() -> Option<SemaphoreGuard<'a>> {
39
let guard_future = OPEN_FILE_LIMITER.acquire();
40
let timeout_future = Timer::after(Duration::from_millis(500));
41
pin_mut!(guard_future);
42
pin_mut!(timeout_future);
43
44
match future::select(guard_future, timeout_future).await {
45
future::Either::Left((guard, _)) => Some(guard),
46
future::Either::Right((_, _)) => None,
47
}
48
}
49
50
struct GuardedFile<'a> {
51
file: File,
52
#[cfg(not(target_os = "windows"))]
53
_guard: Option<SemaphoreGuard<'a>>,
54
#[cfg(target_os = "windows")]
55
_lifetime: PhantomData<&'a ()>,
56
}
57
58
impl<'a> futures_io::AsyncRead for GuardedFile<'a> {
59
fn poll_read(
60
mut self: core::pin::Pin<&mut Self>,
61
cx: &mut core::task::Context<'_>,
62
buf: &mut [u8],
63
) -> core::task::Poll<std::io::Result<usize>> {
64
core::pin::Pin::new(&mut self.file).poll_read(cx, buf)
65
}
66
}
67
68
impl<'a> Reader for GuardedFile<'a> {
69
fn seekable(&mut self) -> Result<&mut dyn SeekableReader, ReaderNotSeekableError> {
70
self.file.seekable()
71
}
72
}
73
74
impl AssetReader for FileAssetReader {
75
async fn read<'a>(&'a self, path: &'a Path) -> Result<impl Reader + 'a, AssetReaderError> {
76
#[cfg(not(target_os = "windows"))]
77
let _guard = maybe_get_semaphore().await;
78
79
let full_path = self.root_path.join(path);
80
File::open(&full_path)
81
.await
82
.map_err(|e| {
83
if e.kind() == std::io::ErrorKind::NotFound {
84
AssetReaderError::NotFound(full_path)
85
} else {
86
e.into()
87
}
88
})
89
.map(|file| GuardedFile {
90
file,
91
#[cfg(not(target_os = "windows"))]
92
_guard,
93
#[cfg(target_os = "windows")]
94
_lifetime: PhantomData,
95
})
96
}
97
98
async fn read_meta<'a>(&'a self, path: &'a Path) -> Result<impl Reader + 'a, AssetReaderError> {
99
#[cfg(not(target_os = "windows"))]
100
let _guard = maybe_get_semaphore().await;
101
102
let meta_path = get_meta_path(path);
103
let full_path = self.root_path.join(meta_path);
104
File::open(&full_path)
105
.await
106
.map_err(|e| {
107
if e.kind() == std::io::ErrorKind::NotFound {
108
AssetReaderError::NotFound(full_path)
109
} else {
110
e.into()
111
}
112
})
113
.map(|file| GuardedFile {
114
file,
115
#[cfg(not(target_os = "windows"))]
116
_guard,
117
#[cfg(target_os = "windows")]
118
_lifetime: PhantomData,
119
})
120
}
121
122
async fn read_directory<'a>(
123
&'a self,
124
path: &'a Path,
125
) -> Result<Box<PathStream>, AssetReaderError> {
126
let full_path = self.root_path.join(path);
127
match read_dir(&full_path).await {
128
Ok(read_dir) => {
129
let root_path = self.root_path.clone();
130
let mapped_stream = read_dir.filter_map(move |f| {
131
f.ok().and_then(|dir_entry| {
132
let path = dir_entry.path();
133
// filter out meta files as they are not considered assets
134
if let Some(ext) = path.extension().and_then(|e| e.to_str())
135
&& ext.eq_ignore_ascii_case("meta")
136
{
137
return None;
138
}
139
// filter out hidden files. they are not listed by default but are directly targetable
140
if path
141
.file_name()
142
.and_then(|file_name| file_name.to_str())
143
.map(|file_name| file_name.starts_with('.'))
144
.unwrap_or_default()
145
{
146
return None;
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(mapped_stream);
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
174
impl 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
async_fs::create_dir_all(parent).await?;
179
}
180
let file = File::create(&full_path).await?;
181
let writer: Box<Writer> = Box::new(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
async_fs::create_dir_all(parent).await?;
190
}
191
let file = File::create(&full_path).await?;
192
let writer: Box<Writer> = Box::new(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
async_fs::remove_file(full_path).await?;
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
async_fs::remove_file(full_path).await?;
206
Ok(())
207
}
208
209
async fn rename<'a>(
210
&'a self,
211
old_path: &'a Path,
212
new_path: &'a Path,
213
) -> Result<(), AssetWriterError> {
214
let full_old_path = self.root_path.join(old_path);
215
let full_new_path = self.root_path.join(new_path);
216
if let Some(parent) = full_new_path.parent() {
217
async_fs::create_dir_all(parent).await?;
218
}
219
async_fs::rename(full_old_path, full_new_path).await?;
220
Ok(())
221
}
222
223
async fn rename_meta<'a>(
224
&'a self,
225
old_path: &'a Path,
226
new_path: &'a Path,
227
) -> Result<(), AssetWriterError> {
228
let old_meta_path = get_meta_path(old_path);
229
let new_meta_path = get_meta_path(new_path);
230
let full_old_path = self.root_path.join(old_meta_path);
231
let full_new_path = self.root_path.join(new_meta_path);
232
if let Some(parent) = full_new_path.parent() {
233
async_fs::create_dir_all(parent).await?;
234
}
235
async_fs::rename(full_old_path, full_new_path).await?;
236
Ok(())
237
}
238
239
async fn create_directory<'a>(&'a self, path: &'a Path) -> Result<(), AssetWriterError> {
240
let full_path = self.root_path.join(path);
241
async_fs::create_dir_all(full_path).await?;
242
Ok(())
243
}
244
245
async fn remove_directory<'a>(&'a self, path: &'a Path) -> Result<(), AssetWriterError> {
246
let full_path = self.root_path.join(path);
247
async_fs::remove_dir_all(full_path).await?;
248
Ok(())
249
}
250
251
async fn remove_empty_directory<'a>(&'a self, path: &'a Path) -> Result<(), AssetWriterError> {
252
let full_path = self.root_path.join(path);
253
async_fs::remove_dir(full_path).await?;
254
Ok(())
255
}
256
257
async fn remove_assets_in_directory<'a>(
258
&'a self,
259
path: &'a Path,
260
) -> Result<(), AssetWriterError> {
261
let full_path = self.root_path.join(path);
262
async_fs::remove_dir_all(&full_path).await?;
263
async_fs::create_dir_all(&full_path).await?;
264
Ok(())
265
}
266
}
267
268