1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
use {
    crate::{Cache, Ready},
    alloc::vec::Vec,
    core::{convert::Infallible, future::Future},
    maybe_sync::{MaybeSend, MaybeSync},
};

#[cfg(feature = "std")]
use std::error::Error;

#[cfg(not(feature = "std"))]
use core::fmt::Display;

/// Loaded, processed and prepared asset.
/// This trait specifies how asset instances can be built from intermediate values
/// that are produced by `Format` implemetations.
///
/// [`Format`]: ./trait.Format.html
pub trait Asset: MaybeSend + MaybeSync + Sized + Clone + 'static {
    /// Error that may occur during asset building.
    #[cfg(feature = "std")]
    type Error: Error + MaybeSend + MaybeSync;

    /// Error that may occur during asset building.
    #[cfg(not(feature = "std"))]
    type Error: Display + MaybeSend + MaybeSync;

    /// Asset processing context.
    /// Instance of context is required to convert asset intermediate representation into asset instance.
    /// Special context type [`PhantomContext`] can be specified if no context is required,
    /// reducing overhead and making asset building occur faster.
    ///
    /// [`PhantomContext`]: ./struct.PhantomContext.html
    type Context;

    /// Intermediate representation type for the asset.
    /// This representation is constructed by [`Format::decode`].
    ///
    /// [`Format::decode`]: ./trait.Format.html#tymethod.decode
    type Repr: MaybeSend;

    /// Asynchronous result produced by asset building.
    type BuildFuture: Future<Output = Result<Self, Self::Error>> + MaybeSend + 'static;

    /// Build asset instance from intermediate representation using provided context.
    fn build(repr: Self::Repr, ctx: &mut Self::Context) -> Self::BuildFuture;
}

/// Format trait interprets raw bytes as an asset.
/// It may also use context for asset instance creation
/// and [`Cache`] to load compound assets.
///
/// [`Cache`]: ./struct.Cache.html
pub trait Format<A: Asset, K>: MaybeSend + 'static {
    /// Error that may occur during asset loading.
    #[cfg(feature = "std")]
    type Error: Error + MaybeSend + MaybeSync + 'static;

    /// Error that may occur during asset loading.
    #[cfg(not(feature = "std"))]
    type Error: Display + MaybeSend + MaybeSync + 'static;

    /// Asynchronous result produced by the format loading.
    type DecodeFuture: Future<Output = Result<A::Repr, Self::Error>> + MaybeSend + 'static;

    /// Decode asset intermediate representation from raw data using cache to fetch sub-assets.
    fn decode(self, bytes: Vec<u8>, cache: &Cache<K>) -> Self::DecodeFuture;
}

/// Default format for given asset type.
/// Allows calling [`Cache::load`] and make it use default format value for loading.
/// Has no effect otherwise.
///
/// [`Cache::load`]: /trait.Cache.html#tymethod.load
pub trait AssetDefaultFormat<K>: Asset {
    /// Format that will be used when asset is loaded using [`Cache::load`]
    ///
    /// [`Cache::load`]: /trait.Cache.html#tymethod.load
    type DefaultFormat: Format<Self, K> + Default;
}

/// Shortcut for implementing [`Asset`] when asset building is synchronous.
///
/// [`Asset`]: ./trait.Asset.html
pub trait SyncAsset: MaybeSend + MaybeSync + Sized + Clone + 'static {
    /// Error that may occur during asset building.
    #[cfg(feature = "std")]
    type Error: Error + MaybeSend + MaybeSync;

    /// Error that may occur during asset building.
    #[cfg(not(feature = "std"))]
    type Error: Display + MaybeSend + MaybeSync;

    /// Asset processing context.
    /// Instance of context is required to convert asset intermediate representation into asset instance.
    type Context;

    /// Intermediate representation type for the asset.
    /// This representation is constructed by [`Format::decode`].
    ///
    /// [`Format::decode`]: /trait.Format.html#tymethod.decode
    type Repr: MaybeSend;

    /// Build asset instance from intermediate representation using provided context.
    fn build(repr: Self::Repr, ctx: &mut Self::Context) -> Result<Self, Self::Error>;
}

impl<S> Asset for S
where
    S: SyncAsset,
{
    type Error = S::Error;
    type Repr = S::Repr;
    type Context = S::Context;
    type BuildFuture = Ready<Result<Self, Self::Error>>;

    #[inline]
    fn build(repr: S::Repr, ctx: &mut S::Context) -> Ready<Result<Self, Self::Error>> {
        Ready(Some(S::build(repr, ctx)))
    }
}

/// Dummy context for assets that doesn't require one.
/// Reduces overhead and allows producing asset faster
/// without waiting for next [`Cache::process`] call.
///
/// [`Cache::process`]: ./struct.Cache.html#method.process
pub struct PhantomContext;

/// Shortcut for implementing [`Asset`] when asset is produced directly by [`Format`]
/// and no building is required.
///
/// [`Asset`]: ./trait.Asset.html
/// [`Format`]: ./trait.Format.html
pub trait SimpleAsset: MaybeSend + MaybeSync + Sized + Clone + 'static {}

impl<S> SyncAsset for S
where
    S: SimpleAsset,
{
    type Error = Infallible;
    type Repr = Self;
    type Context = PhantomContext;

    #[inline]
    fn build(repr: Self, _ctx: &mut PhantomContext) -> Result<Self, Self::Error> {
        Ok(repr)
    }
}