Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_asset/src/io/gated.rs
9424 views
1
use crate::io::{AssetReader, AssetReaderError, PathStream, Reader};
2
use alloc::{boxed::Box, sync::Arc};
3
use async_channel::{Receiver, Sender};
4
use bevy_platform::{collections::HashMap, sync::RwLock};
5
use std::{path::Path, sync::PoisonError};
6
7
/// A "gated" reader that will prevent asset reads from returning until
8
/// a given path has been "opened" using [`GateOpener`].
9
///
10
/// This is built primarily for unit tests.
11
pub struct GatedReader<R: AssetReader> {
12
reader: R,
13
gates: Arc<RwLock<HashMap<Box<Path>, (Sender<()>, Receiver<()>)>>>,
14
}
15
16
impl<R: AssetReader + Clone> Clone for GatedReader<R> {
17
fn clone(&self) -> Self {
18
Self {
19
reader: self.reader.clone(),
20
gates: self.gates.clone(),
21
}
22
}
23
}
24
25
/// Opens path "gates" for a [`GatedReader`].
26
pub struct GateOpener {
27
gates: Arc<RwLock<HashMap<Box<Path>, (Sender<()>, Receiver<()>)>>>,
28
}
29
30
impl GateOpener {
31
/// Opens the `path` "gate", allowing a _single_ [`AssetReader`] operation to return for that path.
32
/// If multiple operations are expected, call `open` the expected number of calls.
33
pub fn open<P: AsRef<Path>>(&self, path: P) {
34
let mut gates = self.gates.write().unwrap_or_else(PoisonError::into_inner);
35
let gates = gates
36
.entry_ref(path.as_ref())
37
.or_insert_with(async_channel::unbounded);
38
gates.0.send_blocking(()).unwrap();
39
}
40
}
41
42
impl<R: AssetReader> GatedReader<R> {
43
/// Creates a new [`GatedReader`], which wraps the given `reader`. Also returns a [`GateOpener`] which
44
/// can be used to open "path gates" for this [`GatedReader`].
45
pub fn new(reader: R) -> (Self, GateOpener) {
46
let gates = Arc::new(RwLock::new(HashMap::default()));
47
(
48
Self {
49
reader,
50
gates: gates.clone(),
51
},
52
GateOpener { gates },
53
)
54
}
55
}
56
57
impl<R: AssetReader> AssetReader for GatedReader<R> {
58
async fn read<'a>(&'a self, path: &'a Path) -> Result<impl Reader + 'a, AssetReaderError> {
59
let receiver = {
60
let mut gates = self.gates.write().unwrap_or_else(PoisonError::into_inner);
61
let gates = gates
62
.entry_ref(path.as_ref())
63
.or_insert_with(async_channel::unbounded);
64
gates.1.clone()
65
};
66
receiver.recv().await.unwrap();
67
let result = self.reader.read(path).await?;
68
Ok(result)
69
}
70
71
async fn read_meta<'a>(&'a self, path: &'a Path) -> Result<impl Reader + 'a, AssetReaderError> {
72
self.reader.read_meta(path).await
73
}
74
75
async fn read_directory<'a>(
76
&'a self,
77
path: &'a Path,
78
) -> Result<Box<PathStream>, AssetReaderError> {
79
self.reader.read_directory(path).await
80
}
81
82
async fn is_directory<'a>(&'a self, path: &'a Path) -> Result<bool, AssetReaderError> {
83
self.reader.is_directory(path).await
84
}
85
}
86
87