Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_asset/src/io/source.rs
9367 views
1
use crate::{
2
io::{processor_gated::ProcessorGatedReader, AssetSourceEvent, AssetWatcher},
3
processor::ProcessingState,
4
};
5
use alloc::{
6
boxed::Box,
7
string::{String, ToString},
8
sync::Arc,
9
};
10
use atomicow::CowArc;
11
use bevy_ecs::resource::Resource;
12
use bevy_platform::collections::HashMap;
13
use core::{fmt::Display, hash::Hash, time::Duration};
14
use thiserror::Error;
15
use tracing::warn;
16
17
use super::{ErasedAssetReader, ErasedAssetWriter};
18
19
/// A reference to an "asset source", which maps to an [`AssetReader`](crate::io::AssetReader) and/or [`AssetWriter`](crate::io::AssetWriter).
20
///
21
/// * [`AssetSourceId::Default`] corresponds to "default asset paths" that don't specify a source: `/path/to/asset.png`
22
/// * [`AssetSourceId::Name`] corresponds to asset paths that _do_ specify a source: `remote://path/to/asset.png`, where `remote` is the name.
23
#[derive(Default, Clone, Debug, Eq)]
24
pub enum AssetSourceId<'a> {
25
/// The default asset source.
26
#[default]
27
Default,
28
/// A non-default named asset source.
29
Name(CowArc<'a, str>),
30
}
31
32
impl<'a> Display for AssetSourceId<'a> {
33
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
34
match self.as_str() {
35
None => write!(f, "AssetSourceId::Default"),
36
Some(v) => write!(f, "AssetSourceId::Name({v})"),
37
}
38
}
39
}
40
41
impl<'a> AssetSourceId<'a> {
42
/// Creates a new [`AssetSourceId`]
43
pub fn new(source: Option<impl Into<CowArc<'a, str>>>) -> AssetSourceId<'a> {
44
match source {
45
Some(source) => AssetSourceId::Name(source.into()),
46
None => AssetSourceId::Default,
47
}
48
}
49
50
/// Returns [`None`] if this is [`AssetSourceId::Default`] and [`Some`] containing the
51
/// name if this is [`AssetSourceId::Name`].
52
pub fn as_str(&self) -> Option<&str> {
53
match self {
54
AssetSourceId::Default => None,
55
AssetSourceId::Name(v) => Some(v),
56
}
57
}
58
59
/// If this is not already an owned / static id, create one. Otherwise, it will return itself (with a static lifetime).
60
pub fn into_owned(self) -> AssetSourceId<'static> {
61
match self {
62
AssetSourceId::Default => AssetSourceId::Default,
63
AssetSourceId::Name(v) => AssetSourceId::Name(v.into_owned()),
64
}
65
}
66
67
/// Clones into an owned [`AssetSourceId<'static>`].
68
/// This is equivalent to `.clone().into_owned()`.
69
#[inline]
70
pub fn clone_owned(&self) -> AssetSourceId<'static> {
71
self.clone().into_owned()
72
}
73
}
74
75
// This is only implemented for static lifetimes to ensure `Path::clone` does not allocate
76
// by ensuring that this is stored as a `CowArc::Static`.
77
// Please read https://github.com/bevyengine/bevy/issues/19844 before changing this!
78
impl From<&'static str> for AssetSourceId<'static> {
79
fn from(value: &'static str) -> Self {
80
AssetSourceId::Name(value.into())
81
}
82
}
83
84
impl<'a, 'b> From<&'a AssetSourceId<'b>> for AssetSourceId<'b> {
85
fn from(value: &'a AssetSourceId<'b>) -> Self {
86
value.clone()
87
}
88
}
89
90
impl From<Option<&'static str>> for AssetSourceId<'static> {
91
fn from(value: Option<&'static str>) -> Self {
92
match value {
93
Some(value) => AssetSourceId::Name(value.into()),
94
None => AssetSourceId::Default,
95
}
96
}
97
}
98
99
impl From<String> for AssetSourceId<'static> {
100
fn from(value: String) -> Self {
101
AssetSourceId::Name(value.into())
102
}
103
}
104
105
impl<'a> Hash for AssetSourceId<'a> {
106
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
107
self.as_str().hash(state);
108
}
109
}
110
111
impl<'a> PartialEq for AssetSourceId<'a> {
112
fn eq(&self, other: &Self) -> bool {
113
self.as_str().eq(&other.as_str())
114
}
115
}
116
117
/// Metadata about an "asset source", such as how to construct the [`AssetReader`](crate::io::AssetReader) and [`AssetWriter`](crate::io::AssetWriter) for the source,
118
/// and whether or not the source is processed.
119
pub struct AssetSourceBuilder {
120
/// The [`ErasedAssetReader`] to use on the unprocessed asset.
121
pub reader: Box<dyn FnMut() -> Box<dyn ErasedAssetReader> + Send + Sync>,
122
/// The [`ErasedAssetWriter`] to use on the unprocessed asset.
123
pub writer: Option<Box<dyn FnMut(bool) -> Option<Box<dyn ErasedAssetWriter>> + Send + Sync>>,
124
/// The [`AssetWatcher`] to use for unprocessed assets, if any.
125
pub watcher: Option<
126
Box<
127
dyn FnMut(async_channel::Sender<AssetSourceEvent>) -> Option<Box<dyn AssetWatcher>>
128
+ Send
129
+ Sync,
130
>,
131
>,
132
/// The [`ErasedAssetReader`] to use for processed assets.
133
pub processed_reader: Option<Box<dyn FnMut() -> Box<dyn ErasedAssetReader> + Send + Sync>>,
134
/// The [`ErasedAssetWriter`] to use for processed assets.
135
pub processed_writer:
136
Option<Box<dyn FnMut(bool) -> Option<Box<dyn ErasedAssetWriter>> + Send + Sync>>,
137
/// The [`AssetWatcher`] to use for processed assets, if any.
138
pub processed_watcher: Option<
139
Box<
140
dyn FnMut(async_channel::Sender<AssetSourceEvent>) -> Option<Box<dyn AssetWatcher>>
141
+ Send
142
+ Sync,
143
>,
144
>,
145
/// The warning message to display when watching an unprocessed asset fails.
146
pub watch_warning: Option<&'static str>,
147
/// The warning message to display when watching a processed asset fails.
148
pub processed_watch_warning: Option<&'static str>,
149
}
150
151
impl AssetSourceBuilder {
152
/// Creates a new builder, starting with the provided reader.
153
pub fn new(
154
reader: impl FnMut() -> Box<dyn ErasedAssetReader> + Send + Sync + 'static,
155
) -> AssetSourceBuilder {
156
Self {
157
reader: Box::new(reader),
158
writer: None,
159
watcher: None,
160
processed_reader: None,
161
processed_writer: None,
162
processed_watcher: None,
163
watch_warning: None,
164
processed_watch_warning: None,
165
}
166
}
167
168
/// Builds a new [`AssetSource`] with the given `id`. If `watch` is true, the unprocessed source will watch for changes.
169
/// If `watch_processed` is true, the processed source will watch for changes.
170
pub fn build(
171
&mut self,
172
id: AssetSourceId<'static>,
173
watch: bool,
174
watch_processed: bool,
175
) -> AssetSource {
176
let reader = self.reader.as_mut()();
177
let writer = self.writer.as_mut().and_then(|w| w(false));
178
let processed_writer = self.processed_writer.as_mut().and_then(|w| w(true));
179
let mut source = AssetSource {
180
id: id.clone(),
181
reader,
182
writer,
183
processed_reader: self
184
.processed_reader
185
.as_mut()
186
.map(|r| r())
187
.map(Into::<Arc<_>>::into),
188
ungated_processed_reader: None,
189
processed_writer,
190
event_receiver: None,
191
watcher: None,
192
processed_event_receiver: None,
193
processed_watcher: None,
194
};
195
196
if watch {
197
let (sender, receiver) = async_channel::unbounded();
198
match self.watcher.as_mut().and_then(|w| w(sender)) {
199
Some(w) => {
200
source.watcher = Some(w);
201
source.event_receiver = Some(receiver);
202
}
203
None => {
204
if let Some(warning) = self.watch_warning {
205
warn!("{id} does not have an AssetWatcher configured. {warning}");
206
}
207
}
208
}
209
}
210
211
if watch_processed {
212
let (sender, receiver) = async_channel::unbounded();
213
match self.processed_watcher.as_mut().and_then(|w| w(sender)) {
214
Some(w) => {
215
source.processed_watcher = Some(w);
216
source.processed_event_receiver = Some(receiver);
217
}
218
None => {
219
if let Some(warning) = self.processed_watch_warning {
220
warn!("{id} does not have a processed AssetWatcher configured. {warning}");
221
}
222
}
223
}
224
}
225
source
226
}
227
228
/// Will use the given `reader` function to construct unprocessed [`AssetReader`](crate::io::AssetReader) instances.
229
pub fn with_reader(
230
mut self,
231
reader: impl FnMut() -> Box<dyn ErasedAssetReader> + Send + Sync + 'static,
232
) -> Self {
233
self.reader = Box::new(reader);
234
self
235
}
236
237
/// Will use the given `writer` function to construct unprocessed [`AssetWriter`](crate::io::AssetWriter) instances.
238
pub fn with_writer(
239
mut self,
240
writer: impl FnMut(bool) -> Option<Box<dyn ErasedAssetWriter>> + Send + Sync + 'static,
241
) -> Self {
242
self.writer = Some(Box::new(writer));
243
self
244
}
245
246
/// Will use the given `watcher` function to construct unprocessed [`AssetWatcher`] instances.
247
pub fn with_watcher(
248
mut self,
249
watcher: impl FnMut(async_channel::Sender<AssetSourceEvent>) -> Option<Box<dyn AssetWatcher>>
250
+ Send
251
+ Sync
252
+ 'static,
253
) -> Self {
254
self.watcher = Some(Box::new(watcher));
255
self
256
}
257
258
/// Will use the given `reader` function to construct processed [`AssetReader`](crate::io::AssetReader) instances.
259
pub fn with_processed_reader(
260
mut self,
261
reader: impl FnMut() -> Box<dyn ErasedAssetReader> + Send + Sync + 'static,
262
) -> Self {
263
self.processed_reader = Some(Box::new(reader));
264
self
265
}
266
267
/// Will use the given `writer` function to construct processed [`AssetWriter`](crate::io::AssetWriter) instances.
268
pub fn with_processed_writer(
269
mut self,
270
writer: impl FnMut(bool) -> Option<Box<dyn ErasedAssetWriter>> + Send + Sync + 'static,
271
) -> Self {
272
self.processed_writer = Some(Box::new(writer));
273
self
274
}
275
276
/// Will use the given `watcher` function to construct processed [`AssetWatcher`] instances.
277
pub fn with_processed_watcher(
278
mut self,
279
watcher: impl FnMut(async_channel::Sender<AssetSourceEvent>) -> Option<Box<dyn AssetWatcher>>
280
+ Send
281
+ Sync
282
+ 'static,
283
) -> Self {
284
self.processed_watcher = Some(Box::new(watcher));
285
self
286
}
287
288
/// Enables a warning for the unprocessed source watcher, which will print when watching is enabled and the unprocessed source doesn't have a watcher.
289
pub fn with_watch_warning(mut self, warning: &'static str) -> Self {
290
self.watch_warning = Some(warning);
291
self
292
}
293
294
/// Enables a warning for the processed source watcher, which will print when watching is enabled and the processed source doesn't have a watcher.
295
pub fn with_processed_watch_warning(mut self, warning: &'static str) -> Self {
296
self.processed_watch_warning = Some(warning);
297
self
298
}
299
300
/// Returns a builder containing the "platform default source" for the given `path` and `processed_path`.
301
/// For most platforms, this will use [`FileAssetReader`](crate::io::file::FileAssetReader) / [`FileAssetWriter`](crate::io::file::FileAssetWriter),
302
/// but some platforms (such as Android) have their own default readers / writers / watchers.
303
pub fn platform_default(path: &str, processed_path: Option<&str>) -> Self {
304
let default = Self::new(AssetSource::get_default_reader(path.to_string()))
305
.with_writer(AssetSource::get_default_writer(path.to_string()))
306
.with_watcher(AssetSource::get_default_watcher(
307
path.to_string(),
308
Duration::from_millis(300),
309
))
310
.with_watch_warning(AssetSource::get_default_watch_warning());
311
if let Some(processed_path) = processed_path {
312
default
313
.with_processed_reader(AssetSource::get_default_reader(processed_path.to_string()))
314
.with_processed_writer(AssetSource::get_default_writer(processed_path.to_string()))
315
.with_processed_watcher(AssetSource::get_default_watcher(
316
processed_path.to_string(),
317
Duration::from_millis(300),
318
))
319
.with_processed_watch_warning(AssetSource::get_default_watch_warning())
320
} else {
321
default
322
}
323
}
324
}
325
326
/// A [`Resource`] that hold (repeatable) functions capable of producing new [`AssetReader`](crate::io::AssetReader) and [`AssetWriter`](crate::io::AssetWriter) instances
327
/// for a given asset source.
328
#[derive(Resource, Default)]
329
pub struct AssetSourceBuilders {
330
sources: HashMap<CowArc<'static, str>, AssetSourceBuilder>,
331
default: Option<AssetSourceBuilder>,
332
}
333
334
impl AssetSourceBuilders {
335
/// Inserts a new builder with the given `id`
336
pub fn insert(&mut self, id: impl Into<AssetSourceId<'static>>, source: AssetSourceBuilder) {
337
match id.into() {
338
AssetSourceId::Default => {
339
self.default = Some(source);
340
}
341
AssetSourceId::Name(name) => {
342
self.sources.insert(name, source);
343
}
344
}
345
}
346
347
/// Gets a mutable builder with the given `id`, if it exists.
348
pub fn get_mut<'a, 'b>(
349
&'a mut self,
350
id: impl Into<AssetSourceId<'b>>,
351
) -> Option<&'a mut AssetSourceBuilder> {
352
match id.into() {
353
AssetSourceId::Default => self.default.as_mut(),
354
AssetSourceId::Name(name) => self.sources.get_mut(&name.into_owned()),
355
}
356
}
357
358
/// Builds a new [`AssetSources`] collection. If `watch` is true, the unprocessed sources will watch for changes.
359
/// If `watch_processed` is true, the processed sources will watch for changes.
360
pub fn build_sources(&mut self, watch: bool, watch_processed: bool) -> AssetSources {
361
let mut sources = <HashMap<_, _>>::default();
362
for (id, source) in &mut self.sources {
363
let source = source.build(
364
AssetSourceId::Name(id.clone_owned()),
365
watch,
366
watch_processed,
367
);
368
sources.insert(id.clone_owned(), source);
369
}
370
371
AssetSources {
372
sources,
373
default: self
374
.default
375
.as_mut()
376
.map(|p| p.build(AssetSourceId::Default, watch, watch_processed))
377
.expect(MISSING_DEFAULT_SOURCE),
378
}
379
}
380
381
/// Initializes the default [`AssetSourceBuilder`] if it has not already been set.
382
pub fn init_default_source(&mut self, path: &str, processed_path: Option<&str>) {
383
self.default
384
.get_or_insert_with(|| AssetSourceBuilder::platform_default(path, processed_path));
385
}
386
}
387
388
/// A collection of unprocessed and processed [`AssetReader`](crate::io::AssetReader), [`AssetWriter`](crate::io::AssetWriter), and [`AssetWatcher`] instances
389
/// for a specific asset source, identified by an [`AssetSourceId`].
390
pub struct AssetSource {
391
id: AssetSourceId<'static>,
392
reader: Box<dyn ErasedAssetReader>,
393
writer: Option<Box<dyn ErasedAssetWriter>>,
394
processed_reader: Option<Arc<dyn ErasedAssetReader>>,
395
/// The ungated version of `processed_reader`.
396
///
397
/// This allows the processor to read all the processed assets to initialize itself without
398
/// being gated on itself (causing a deadlock).
399
ungated_processed_reader: Option<Arc<dyn ErasedAssetReader>>,
400
processed_writer: Option<Box<dyn ErasedAssetWriter>>,
401
watcher: Option<Box<dyn AssetWatcher>>,
402
processed_watcher: Option<Box<dyn AssetWatcher>>,
403
event_receiver: Option<async_channel::Receiver<AssetSourceEvent>>,
404
processed_event_receiver: Option<async_channel::Receiver<AssetSourceEvent>>,
405
}
406
407
impl AssetSource {
408
/// Returns this source's id.
409
#[inline]
410
pub fn id(&self) -> AssetSourceId<'static> {
411
self.id.clone()
412
}
413
414
/// Return's this source's unprocessed [`AssetReader`](crate::io::AssetReader).
415
#[inline]
416
pub fn reader(&self) -> &dyn ErasedAssetReader {
417
&*self.reader
418
}
419
420
/// Return's this source's unprocessed [`AssetWriter`](crate::io::AssetWriter), if it exists.
421
#[inline]
422
pub fn writer(&self) -> Result<&dyn ErasedAssetWriter, MissingAssetWriterError> {
423
self.writer
424
.as_deref()
425
.ok_or_else(|| MissingAssetWriterError(self.id.clone_owned()))
426
}
427
428
/// Return's this source's processed [`AssetReader`](crate::io::AssetReader), if it exists.
429
#[inline]
430
pub fn processed_reader(
431
&self,
432
) -> Result<&dyn ErasedAssetReader, MissingProcessedAssetReaderError> {
433
self.processed_reader
434
.as_deref()
435
.ok_or_else(|| MissingProcessedAssetReaderError(self.id.clone_owned()))
436
}
437
438
/// Return's this source's ungated processed [`AssetReader`](crate::io::AssetReader), if it
439
/// exists.
440
#[inline]
441
pub(crate) fn ungated_processed_reader(&self) -> Option<&dyn ErasedAssetReader> {
442
self.ungated_processed_reader.as_deref()
443
}
444
445
/// Return's this source's processed [`AssetWriter`](crate::io::AssetWriter), if it exists.
446
#[inline]
447
pub fn processed_writer(
448
&self,
449
) -> Result<&dyn ErasedAssetWriter, MissingProcessedAssetWriterError> {
450
self.processed_writer
451
.as_deref()
452
.ok_or_else(|| MissingProcessedAssetWriterError(self.id.clone_owned()))
453
}
454
455
/// Return's this source's unprocessed event receiver, if the source is currently watching for changes.
456
#[inline]
457
pub fn event_receiver(&self) -> Option<&async_channel::Receiver<AssetSourceEvent>> {
458
self.event_receiver.as_ref()
459
}
460
461
/// Return's this source's processed event receiver, if the source is currently watching for changes.
462
#[inline]
463
pub fn processed_event_receiver(&self) -> Option<&async_channel::Receiver<AssetSourceEvent>> {
464
self.processed_event_receiver.as_ref()
465
}
466
467
/// Returns true if the assets in this source should be processed.
468
#[inline]
469
pub fn should_process(&self) -> bool {
470
self.processed_writer.is_some()
471
}
472
473
/// Returns a builder function for this platform's default [`AssetReader`](crate::io::AssetReader). `path` is the relative path to
474
/// the asset root.
475
pub fn get_default_reader(
476
_path: String,
477
) -> impl FnMut() -> Box<dyn ErasedAssetReader> + Send + Sync {
478
move || {
479
#[cfg(all(not(target_arch = "wasm32"), not(target_os = "android")))]
480
return Box::new(super::file::FileAssetReader::new(&_path));
481
#[cfg(target_arch = "wasm32")]
482
return Box::new(super::wasm::HttpWasmAssetReader::new(&_path));
483
#[cfg(target_os = "android")]
484
return Box::new(super::android::AndroidAssetReader);
485
}
486
}
487
488
/// Returns a builder function for this platform's default [`AssetWriter`](crate::io::AssetWriter). `path` is the relative path to
489
/// the asset root. This will return [`None`] if this platform does not support writing assets by default.
490
pub fn get_default_writer(
491
_path: String,
492
) -> impl FnMut(bool) -> Option<Box<dyn ErasedAssetWriter>> + Send + Sync {
493
move |_create_root: bool| {
494
#[cfg(all(not(target_arch = "wasm32"), not(target_os = "android")))]
495
return Some(Box::new(super::file::FileAssetWriter::new(
496
&_path,
497
_create_root,
498
)));
499
#[cfg(any(target_arch = "wasm32", target_os = "android"))]
500
return None;
501
}
502
}
503
504
/// Returns the default non-existent [`AssetWatcher`] warning for the current platform.
505
pub fn get_default_watch_warning() -> &'static str {
506
#[cfg(target_arch = "wasm32")]
507
return "Web does not currently support watching assets.";
508
#[cfg(target_os = "android")]
509
return "Android does not currently support watching assets.";
510
#[cfg(all(
511
not(target_arch = "wasm32"),
512
not(target_os = "android"),
513
not(feature = "file_watcher")
514
))]
515
return "Consider enabling the `file_watcher` feature.";
516
#[cfg(all(
517
not(target_arch = "wasm32"),
518
not(target_os = "android"),
519
feature = "file_watcher"
520
))]
521
return "Consider adding an \"assets\" directory.";
522
}
523
524
/// Returns a builder function for this platform's default [`AssetWatcher`]. `path` is the relative path to
525
/// the asset root. This will return [`None`] if this platform does not support watching assets by default.
526
/// `file_debounce_time` is the amount of time to wait (and debounce duplicate events) before returning an event.
527
/// Higher durations reduce duplicates but increase the amount of time before a change event is processed. If the
528
/// duration is set too low, some systems might surface events _before_ their filesystem has the changes.
529
#[cfg_attr(
530
any(
531
not(feature = "file_watcher"),
532
target_arch = "wasm32",
533
target_os = "android"
534
),
535
expect(
536
unused_variables,
537
reason = "The `path` and `file_debounce_wait_time` arguments are unused when on WASM, Android, or if the `file_watcher` feature is disabled."
538
)
539
)]
540
pub fn get_default_watcher(
541
path: String,
542
file_debounce_wait_time: Duration,
543
) -> impl FnMut(async_channel::Sender<AssetSourceEvent>) -> Option<Box<dyn AssetWatcher>> + Send + Sync
544
{
545
move |sender: async_channel::Sender<AssetSourceEvent>| {
546
#[cfg(all(
547
feature = "file_watcher",
548
not(target_arch = "wasm32"),
549
not(target_os = "android")
550
))]
551
{
552
let path = super::file::get_base_path().join(path.clone());
553
if path.exists() {
554
Some(Box::new(
555
super::file::FileWatcher::new(
556
path.clone(),
557
sender,
558
file_debounce_wait_time,
559
)
560
.unwrap_or_else(|e| {
561
panic!("Failed to create file watcher from path {path:?}, {e:?}")
562
}),
563
))
564
} else {
565
warn!("Skip creating file watcher because path {path:?} does not exist.");
566
None
567
}
568
}
569
#[cfg(any(
570
not(feature = "file_watcher"),
571
target_arch = "wasm32",
572
target_os = "android"
573
))]
574
return None;
575
}
576
}
577
578
/// This will cause processed [`AssetReader`](crate::io::AssetReader) futures (such as [`AssetReader::read`](crate::io::AssetReader::read)) to wait until
579
/// the [`AssetProcessor`](crate::AssetProcessor) has finished processing the requested asset.
580
pub(crate) fn gate_on_processor(&mut self, processing_state: Arc<ProcessingState>) {
581
if let Some(reader) = self.processed_reader.take() {
582
self.ungated_processed_reader = Some(reader.clone());
583
self.processed_reader = Some(Arc::new(ProcessorGatedReader::new(
584
self.id(),
585
reader,
586
processing_state,
587
)));
588
}
589
}
590
}
591
592
/// A collection of [`AssetSource`]s.
593
pub struct AssetSources {
594
sources: HashMap<CowArc<'static, str>, AssetSource>,
595
default: AssetSource,
596
}
597
598
impl AssetSources {
599
/// Gets the [`AssetSource`] with the given `id`, if it exists.
600
pub fn get<'a, 'b>(
601
&'a self,
602
id: impl Into<AssetSourceId<'b>>,
603
) -> Result<&'a AssetSource, MissingAssetSourceError> {
604
match id.into().into_owned() {
605
AssetSourceId::Default => Ok(&self.default),
606
AssetSourceId::Name(name) => self
607
.sources
608
.get(&name)
609
.ok_or(MissingAssetSourceError(AssetSourceId::Name(name))),
610
}
611
}
612
613
/// Iterates all asset sources in the collection (including the default source).
614
pub fn iter(&self) -> impl Iterator<Item = &AssetSource> {
615
self.sources.values().chain(Some(&self.default))
616
}
617
618
/// Mutably iterates all asset sources in the collection (including the default source).
619
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut AssetSource> {
620
self.sources.values_mut().chain(Some(&mut self.default))
621
}
622
623
/// Iterates all processed asset sources in the collection (including the default source).
624
pub fn iter_processed(&self) -> impl Iterator<Item = &AssetSource> {
625
self.iter().filter(|p| p.should_process())
626
}
627
628
/// Mutably iterates all processed asset sources in the collection (including the default source).
629
pub fn iter_processed_mut(&mut self) -> impl Iterator<Item = &mut AssetSource> {
630
self.iter_mut().filter(|p| p.should_process())
631
}
632
633
/// Iterates over the [`AssetSourceId`] of every [`AssetSource`] in the collection (including the default source).
634
pub fn ids(&self) -> impl Iterator<Item = AssetSourceId<'static>> + '_ {
635
self.sources
636
.keys()
637
.map(|k| AssetSourceId::Name(k.clone_owned()))
638
.chain(Some(AssetSourceId::Default))
639
}
640
641
/// This will cause processed [`AssetReader`](crate::io::AssetReader) futures (such as [`AssetReader::read`](crate::io::AssetReader::read)) to wait until
642
/// the [`AssetProcessor`](crate::AssetProcessor) has finished processing the requested asset.
643
pub(crate) fn gate_on_processor(&mut self, processing_state: Arc<ProcessingState>) {
644
for source in self.iter_processed_mut() {
645
source.gate_on_processor(processing_state.clone());
646
}
647
}
648
}
649
650
/// An error returned when an [`AssetSource`] does not exist for a given id.
651
#[derive(Error, Debug, Clone, PartialEq, Eq)]
652
#[error("Asset Source '{0}' does not exist")]
653
pub struct MissingAssetSourceError(AssetSourceId<'static>);
654
655
/// An error returned when an [`AssetWriter`](crate::io::AssetWriter) does not exist for a given id.
656
#[derive(Error, Debug, Clone)]
657
#[error("Asset Source '{0}' does not have an AssetWriter.")]
658
pub struct MissingAssetWriterError(AssetSourceId<'static>);
659
660
/// An error returned when a processed [`AssetReader`](crate::io::AssetReader) does not exist for a given id.
661
#[derive(Error, Debug, Clone, PartialEq, Eq)]
662
#[error("Asset Source '{0}' does not have a processed AssetReader.")]
663
pub struct MissingProcessedAssetReaderError(AssetSourceId<'static>);
664
665
/// An error returned when a processed [`AssetWriter`](crate::io::AssetWriter) does not exist for a given id.
666
#[derive(Error, Debug, Clone)]
667
#[error("Asset Source '{0}' does not have a processed AssetWriter.")]
668
pub struct MissingProcessedAssetWriterError(AssetSourceId<'static>);
669
670
const MISSING_DEFAULT_SOURCE: &str =
671
"A default AssetSource is required. Add one to `AssetSourceBuilders`";
672
673