1use crate::io::{
2 AssetReader, AssetReaderError, AssetWriter, AssetWriterError, PathStream, Reader,
3 ReaderRequiredFeatures,
4};
5use alloc::{borrow::ToOwned, boxed::Box, sync::Arc, vec, vec::Vec};
6use bevy_platform::{
7 collections::HashMap,
8 sync::{PoisonError, RwLock},
9};
10use core::{pin::Pin, task::Poll};
11use futures_io::{AsyncRead, AsyncWrite};
12use futures_lite::Stream;
13use std::{
14 io::{Error, ErrorKind, SeekFrom},
15 path::{Path, PathBuf},
16};
17
18use super::AsyncSeek;
19
20#[derive(Default, Debug)]
21struct DirInternal {
22 assets: HashMap<Box<str>, Data>,
23 metadata: HashMap<Box<str>, Data>,
24 dirs: HashMap<Box<str>, Dir>,
25 path: PathBuf,
26}
27
28#[derive(Default, Clone, Debug)]
31pub struct Dir(Arc<RwLock<DirInternal>>);
32
33impl Dir {
34 pub fn new(path: PathBuf) -> Self {
36 Self(Arc::new(RwLock::new(DirInternal {
37 path,
38 ..Default::default()
39 })))
40 }
41
42 pub fn insert_asset_text(&self, path: &Path, asset: &str) {
43 self.insert_asset(path, asset.as_bytes().to_vec());
44 }
45
46 pub fn insert_meta_text(&self, path: &Path, asset: &str) {
47 self.insert_meta(path, asset.as_bytes().to_vec());
48 }
49
50 pub fn insert_asset(&self, path: &Path, value: impl Into<Value>) {
51 let mut dir = self.clone();
52 if let Some(parent) = path.parent() {
53 dir = self.get_or_insert_dir(parent);
54 }
55 dir.0
56 .write()
57 .unwrap_or_else(PoisonError::into_inner)
58 .assets
59 .insert(
60 path.file_name().unwrap().to_string_lossy().into(),
61 Data {
62 value: value.into(),
63 path: path.to_owned(),
64 },
65 );
66 }
67
68 pub fn remove_asset(&self, path: &Path) -> Option<Data> {
72 let mut dir = self.clone();
73 if let Some(parent) = path.parent() {
74 dir = self.get_or_insert_dir(parent);
75 }
76 let key: Box<str> = path.file_name().unwrap().to_string_lossy().into();
77 dir.0
78 .write()
79 .unwrap_or_else(PoisonError::into_inner)
80 .assets
81 .remove(&key)
82 }
83
84 pub fn insert_meta(&self, path: &Path, value: impl Into<Value>) {
85 let mut dir = self.clone();
86 if let Some(parent) = path.parent() {
87 dir = self.get_or_insert_dir(parent);
88 }
89 dir.0
90 .write()
91 .unwrap_or_else(PoisonError::into_inner)
92 .metadata
93 .insert(
94 path.file_name().unwrap().to_string_lossy().into(),
95 Data {
96 value: value.into(),
97 path: path.to_owned(),
98 },
99 );
100 }
101
102 pub fn remove_metadata(&self, path: &Path) -> Option<Data> {
106 let mut dir = self.clone();
107 if let Some(parent) = path.parent() {
108 dir = self.get_or_insert_dir(parent);
109 }
110 let key: Box<str> = path.file_name().unwrap().to_string_lossy().into();
111 dir.0
112 .write()
113 .unwrap_or_else(PoisonError::into_inner)
114 .metadata
115 .remove(&key)
116 }
117
118 pub fn get_or_insert_dir(&self, path: &Path) -> Dir {
119 let mut dir = self.clone();
120 let mut full_path = PathBuf::new();
121 for c in path.components() {
122 full_path.push(c);
123 let name = c.as_os_str().to_string_lossy().into();
124 dir = {
125 let dirs = &mut dir.0.write().unwrap_or_else(PoisonError::into_inner).dirs;
126 dirs.entry(name)
127 .or_insert_with(|| Dir::new(full_path.clone()))
128 .clone()
129 };
130 }
131
132 dir
133 }
134
135 pub fn remove_dir(&self, path: &Path) -> Option<Dir> {
139 let mut dir = self.clone();
140 if let Some(parent) = path.parent() {
141 dir = self.get_or_insert_dir(parent);
142 }
143 let key: Box<str> = path.file_name().unwrap().to_string_lossy().into();
144 dir.0
145 .write()
146 .unwrap_or_else(PoisonError::into_inner)
147 .dirs
148 .remove(&key)
149 }
150
151 pub fn get_dir(&self, path: &Path) -> Option<Dir> {
152 let mut dir = self.clone();
153 for p in path.components() {
154 let component = p.as_os_str().to_str().unwrap();
155 let next_dir = dir
156 .0
157 .read()
158 .unwrap_or_else(PoisonError::into_inner)
159 .dirs
160 .get(component)?
161 .clone();
162 dir = next_dir;
163 }
164 Some(dir)
165 }
166
167 pub fn get_asset(&self, path: &Path) -> Option<Data> {
168 let mut dir = self.clone();
169 if let Some(parent) = path.parent() {
170 dir = dir.get_dir(parent)?;
171 }
172
173 path.file_name().and_then(|f| {
174 dir.0
175 .read()
176 .unwrap_or_else(PoisonError::into_inner)
177 .assets
178 .get(f.to_str().unwrap())
179 .cloned()
180 })
181 }
182
183 pub fn get_metadata(&self, path: &Path) -> Option<Data> {
184 let mut dir = self.clone();
185 if let Some(parent) = path.parent() {
186 dir = dir.get_dir(parent)?;
187 }
188
189 path.file_name().and_then(|f| {
190 dir.0
191 .read()
192 .unwrap_or_else(PoisonError::into_inner)
193 .metadata
194 .get(f.to_str().unwrap())
195 .cloned()
196 })
197 }
198
199 pub fn path(&self) -> PathBuf {
200 self.0
201 .read()
202 .unwrap_or_else(PoisonError::into_inner)
203 .path
204 .to_owned()
205 }
206}
207
208pub struct DirStream {
209 dir: Dir,
210 index: usize,
211 dir_index: usize,
212}
213
214impl DirStream {
215 fn new(dir: Dir) -> Self {
216 Self {
217 dir,
218 index: 0,
219 dir_index: 0,
220 }
221 }
222}
223
224impl Stream for DirStream {
225 type Item = PathBuf;
226
227 fn poll_next(
228 self: Pin<&mut Self>,
229 _cx: &mut core::task::Context<'_>,
230 ) -> Poll<Option<Self::Item>> {
231 let this = self.get_mut();
232 let dir = this.dir.0.read().unwrap_or_else(PoisonError::into_inner);
233
234 let dir_index = this.dir_index;
235 if let Some(dir_path) = dir
236 .dirs
237 .keys()
238 .nth(dir_index)
239 .map(|d| dir.path.join(d.as_ref()))
240 {
241 this.dir_index += 1;
242 Poll::Ready(Some(dir_path))
243 } else {
244 let index = this.index;
245 this.index += 1;
246 Poll::Ready(dir.assets.values().nth(index).map(|d| d.path().to_owned()))
247 }
248 }
249}
250
251#[derive(Default, Clone)]
254pub struct MemoryAssetReader {
255 pub root: Dir,
256}
257
258#[derive(Default, Clone)]
262pub struct MemoryAssetWriter {
263 pub root: Dir,
264}
265
266#[derive(Clone, Debug)]
268pub struct Data {
269 path: PathBuf,
270 value: Value,
271}
272
273#[derive(Clone, Debug)]
275pub enum Value {
276 Vec(Arc<Vec<u8>>),
277 Static(&'static [u8]),
278}
279
280impl Data {
281 pub fn path(&self) -> &Path {
283 &self.path
284 }
285
286 pub fn value(&self) -> &[u8] {
288 match &self.value {
289 Value::Vec(vec) => vec,
290 Value::Static(value) => value,
291 }
292 }
293}
294
295impl From<Vec<u8>> for Value {
296 fn from(value: Vec<u8>) -> Self {
297 Self::Vec(Arc::new(value))
298 }
299}
300
301impl From<&'static [u8]> for Value {
302 fn from(value: &'static [u8]) -> Self {
303 Self::Static(value)
304 }
305}
306
307impl<const N: usize> From<&'static [u8; N]> for Value {
308 fn from(value: &'static [u8; N]) -> Self {
309 Self::Static(value)
310 }
311}
312
313struct DataReader {
314 data: Data,
315 bytes_read: usize,
316}
317
318impl AsyncRead for DataReader {
319 fn poll_read(
320 self: Pin<&mut Self>,
321 _cx: &mut core::task::Context<'_>,
322 buf: &mut [u8],
323 ) -> Poll<futures_io::Result<usize>> {
324 let this = self.get_mut();
326 Poll::Ready(Ok(crate::io::slice_read(
327 this.data.value(),
328 &mut this.bytes_read,
329 buf,
330 )))
331 }
332}
333
334impl AsyncSeek for DataReader {
335 fn poll_seek(
336 self: Pin<&mut Self>,
337 _cx: &mut core::task::Context<'_>,
338 pos: SeekFrom,
339 ) -> Poll<std::io::Result<u64>> {
340 let this = self.get_mut();
342 Poll::Ready(crate::io::slice_seek(
343 this.data.value(),
344 &mut this.bytes_read,
345 pos,
346 ))
347 }
348}
349
350impl Reader for DataReader {
351 fn read_to_end<'a>(
352 &'a mut self,
353 buf: &'a mut Vec<u8>,
354 ) -> stackfuture::StackFuture<'a, std::io::Result<usize>, { super::STACK_FUTURE_SIZE }> {
355 crate::io::read_to_end(self.data.value(), &mut self.bytes_read, buf)
356 }
357}
358
359impl AssetReader for MemoryAssetReader {
360 async fn read<'a>(
361 &'a self,
362 path: &'a Path,
363 _required_features: ReaderRequiredFeatures,
364 ) -> Result<impl Reader + 'a, AssetReaderError> {
365 self.root
366 .get_asset(path)
367 .map(|data| DataReader {
368 data,
369 bytes_read: 0,
370 })
371 .ok_or_else(|| AssetReaderError::NotFound(path.to_path_buf()))
372 }
373
374 async fn read_meta<'a>(&'a self, path: &'a Path) -> Result<impl Reader + 'a, AssetReaderError> {
375 self.root
376 .get_metadata(path)
377 .map(|data| DataReader {
378 data,
379 bytes_read: 0,
380 })
381 .ok_or_else(|| AssetReaderError::NotFound(path.to_path_buf()))
382 }
383
384 async fn read_directory<'a>(
385 &'a self,
386 path: &'a Path,
387 ) -> Result<Box<PathStream>, AssetReaderError> {
388 self.root
389 .get_dir(path)
390 .map(|dir| {
391 let stream: Box<PathStream> = Box::new(DirStream::new(dir));
392 stream
393 })
394 .ok_or_else(|| AssetReaderError::NotFound(path.to_path_buf()))
395 }
396
397 async fn is_directory<'a>(&'a self, path: &'a Path) -> Result<bool, AssetReaderError> {
398 Ok(self.root.get_dir(path).is_some())
399 }
400}
401
402struct DataWriter {
404 dir: Dir,
406 path: PathBuf,
408 current_data: Vec<u8>,
412 is_meta_writer: bool,
414}
415
416impl AsyncWrite for DataWriter {
417 fn poll_write(
418 self: Pin<&mut Self>,
419 _: &mut core::task::Context<'_>,
420 buf: &[u8],
421 ) -> Poll<std::io::Result<usize>> {
422 self.get_mut().current_data.extend_from_slice(buf);
423 Poll::Ready(Ok(buf.len()))
424 }
425
426 fn poll_flush(
427 self: Pin<&mut Self>,
428 _: &mut core::task::Context<'_>,
429 ) -> Poll<std::io::Result<()>> {
430 if self.is_meta_writer {
432 self.dir.insert_meta(&self.path, self.current_data.clone());
433 } else {
434 self.dir.insert_asset(&self.path, self.current_data.clone());
435 }
436 Poll::Ready(Ok(()))
437 }
438
439 fn poll_close(
440 self: Pin<&mut Self>,
441 cx: &mut core::task::Context<'_>,
442 ) -> Poll<std::io::Result<()>> {
443 self.poll_flush(cx)
445 }
446}
447
448impl AssetWriter for MemoryAssetWriter {
449 async fn write<'a>(&'a self, path: &'a Path) -> Result<Box<super::Writer>, AssetWriterError> {
450 Ok(Box::new(DataWriter {
451 dir: self.root.clone(),
452 path: path.to_owned(),
453 current_data: vec![],
454 is_meta_writer: false,
455 }))
456 }
457
458 async fn write_meta<'a>(
459 &'a self,
460 path: &'a Path,
461 ) -> Result<Box<super::Writer>, AssetWriterError> {
462 Ok(Box::new(DataWriter {
463 dir: self.root.clone(),
464 path: path.to_owned(),
465 current_data: vec![],
466 is_meta_writer: true,
467 }))
468 }
469
470 async fn remove<'a>(&'a self, path: &'a Path) -> Result<(), AssetWriterError> {
471 if self.root.remove_asset(path).is_none() {
472 return Err(AssetWriterError::Io(Error::new(
473 ErrorKind::NotFound,
474 "no such file",
475 )));
476 }
477 Ok(())
478 }
479
480 async fn remove_meta<'a>(&'a self, path: &'a Path) -> Result<(), AssetWriterError> {
481 self.root.remove_metadata(path);
482 Ok(())
483 }
484
485 async fn rename<'a>(
486 &'a self,
487 old_path: &'a Path,
488 new_path: &'a Path,
489 ) -> Result<(), AssetWriterError> {
490 let Some(old_asset) = self.root.get_asset(old_path) else {
491 return Err(AssetWriterError::Io(Error::new(
492 ErrorKind::NotFound,
493 "no such file",
494 )));
495 };
496 self.root.insert_asset(new_path, old_asset.value);
497 self.root.remove_asset(old_path);
501 Ok(())
502 }
503
504 async fn rename_meta<'a>(
505 &'a self,
506 old_path: &'a Path,
507 new_path: &'a Path,
508 ) -> Result<(), AssetWriterError> {
509 let Some(old_meta) = self.root.get_metadata(old_path) else {
510 return Err(AssetWriterError::Io(Error::new(
511 ErrorKind::NotFound,
512 "no such file",
513 )));
514 };
515 self.root.insert_meta(new_path, old_meta.value);
516 self.root.remove_metadata(old_path);
520 Ok(())
521 }
522
523 async fn create_directory<'a>(&'a self, path: &'a Path) -> Result<(), AssetWriterError> {
524 self.root.get_or_insert_dir(path);
527 Ok(())
528 }
529
530 async fn remove_directory<'a>(&'a self, path: &'a Path) -> Result<(), AssetWriterError> {
531 if self.root.remove_dir(path).is_none() {
532 return Err(AssetWriterError::Io(Error::new(
533 ErrorKind::NotFound,
534 "no such dir",
535 )));
536 }
537 Ok(())
538 }
539
540 async fn remove_empty_directory<'a>(&'a self, path: &'a Path) -> Result<(), AssetWriterError> {
541 let Some(dir) = self.root.get_dir(path) else {
542 return Err(AssetWriterError::Io(Error::new(
543 ErrorKind::NotFound,
544 "no such dir",
545 )));
546 };
547
548 let dir = dir.0.read().unwrap();
549 if !dir.assets.is_empty() || !dir.metadata.is_empty() || !dir.dirs.is_empty() {
550 return Err(AssetWriterError::Io(Error::new(
551 ErrorKind::DirectoryNotEmpty,
552 "not empty",
553 )));
554 }
555
556 self.root.remove_dir(path);
557 Ok(())
558 }
559
560 async fn remove_assets_in_directory<'a>(
561 &'a self,
562 path: &'a Path,
563 ) -> Result<(), AssetWriterError> {
564 let Some(dir) = self.root.get_dir(path) else {
565 return Err(AssetWriterError::Io(Error::new(
566 ErrorKind::NotFound,
567 "no such dir",
568 )));
569 };
570
571 let mut dir = dir.0.write().unwrap();
572 dir.assets.clear();
573 dir.dirs.clear();
574 dir.metadata.clear();
575 Ok(())
576 }
577}
578
579#[cfg(test)]
580pub mod test {
581 use super::Dir;
582 use std::path::Path;
583
584 #[test]
585 fn memory_dir() {
586 let dir = Dir::default();
587 let a_path = Path::new("a.txt");
588 let a_data = "a".as_bytes().to_vec();
589 let a_meta = "ameta".as_bytes().to_vec();
590
591 dir.insert_asset(a_path, a_data.clone());
592 let asset = dir.get_asset(a_path).unwrap();
593 assert_eq!(asset.path(), a_path);
594 assert_eq!(asset.value(), a_data);
595
596 dir.insert_meta(a_path, a_meta.clone());
597 let meta = dir.get_metadata(a_path).unwrap();
598 assert_eq!(meta.path(), a_path);
599 assert_eq!(meta.value(), a_meta);
600
601 let b_path = Path::new("x/y/b.txt");
602 let b_data = "b".as_bytes().to_vec();
603 let b_meta = "meta".as_bytes().to_vec();
604 dir.insert_asset(b_path, b_data.clone());
605 dir.insert_meta(b_path, b_meta.clone());
606
607 let asset = dir.get_asset(b_path).unwrap();
608 assert_eq!(asset.path(), b_path);
609 assert_eq!(asset.value(), b_data);
610
611 let meta = dir.get_metadata(b_path).unwrap();
612 assert_eq!(meta.path(), b_path);
613 assert_eq!(meta.value(), b_meta);
614 }
615}