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
//! Generic asset loading definition
//!
//! See trait [`Loader`] for more informations
//!
//! [`Loader`]: trait.Loader.html

use std::{
    error::Error,
    str::FromStr,
};

/// Specifies how an asset is loaded.
///
/// With this trait, you can easily specify how you want your data to be loaded.
///
/// # Basic usage
///
/// Most of the time, you don't need to implement this trait yourself, as there
/// are implementations for the most formats (using `serde`). Don't forget to
/// enable the corresponding feature !
///
/// ```no_run
/// # cfg_if::cfg_if! { if #[cfg(feature = "ron")] {
/// use serde::Deserialize;
/// use assets_manager::{Asset, loader};
///
/// // The struct you want to load
/// #[derive(Deserialize)]
/// struct Point {
///     x: i32,
///     y: i32,
/// }
///
/// impl Asset for Point {
///     const EXT: &'static str = "ron";
///
///     // Specify here how to convert raw data
///     type Loader = loader::RonLoader;
/// }
/// # }}
/// ```
pub trait Loader<T> {
    /// Loads an asset from its raw bytes representation.
    fn load(content: Vec<u8>) -> Result<T, Box<dyn Error + Send + Sync>>;
}

/// A [`Loader`] to override [`Asset::load_from_raw`] function without caring
/// about the required `Loader` type.
///
/// Use it when you want to implement [`Asset`] but do not want/need to use a
/// loader, and only to override [`Asset::load_from_raw`].
///
/// **Warning**: this loader is not meant to be called and will panic if so
///
/// [`Loader`]: trait.Loader.html
/// [`Asset`]: ../trait.Asset.html
/// [`Asset::load`]: ../trait.Asset.html#method.load
/// [`Asset::load_from_raw`]: ../trait.Asset.html#method.load_from_raw
#[derive(Debug)]
pub struct CustomLoader;
impl<T> Loader<T> for CustomLoader {
    fn load(_: Vec<u8>) -> Result<T, Box<dyn Error + Send + Sync>> {
        panic!("You forgot to override `Asset::load_from_raw` function")
    }
}

/// Loads assets as a String.
///
/// The file content is assumed to be valid UTF-8.
#[derive(Debug)]
#[deprecated = "This Loader cannot be used to implement the Asset trait and will be removed in a future release"]
pub struct StringLoader;
#[allow(deprecated)]
impl Loader<String> for StringLoader {
    fn load(content: Vec<u8>) -> Result<String, Box<dyn Error + Send + Sync>> {
        Ok(String::from_utf8(content)?)
    }
}

/// Loads assets that can be parsed with `FromStr`.
///
/// Do not use this loader to load `String`s, prefer using [`StringLoader`],
/// which is more efficient.
///
/// If you want your custom type to work with this loader, make sure that
/// `FromStr::Err` meet the requirements
///
/// See trait [`Loader`] for more informations.
///
/// [`StringLoader`]: struct.StringLoader.html
/// [`Loader`]: trait.Loader.html
#[derive(Debug)]
pub struct ParseLoader;
impl<T> Loader<T> for ParseLoader
where
    T: FromStr,
    <T as FromStr>::Err: Error + Send + Sync + 'static,
{
    fn load(content: Vec<u8>) -> Result<T, Box<dyn Error + Send + Sync>> {
        let string = String::from_utf8(content)?;
        Ok(string.parse()?)
    }
}

macro_rules! serde_loader {
    ($feature:literal, $doc:literal, $name:ident, $fun:path) => {
        #[doc = $doc]
        ///
        /// See trait [`Loader`] for more informations.
        ///
        /// [`Loader`]: trait.Loader.html
        #[cfg(feature = $feature)]
        #[cfg_attr(docsrs, doc(cfg(feature = $feature)))]
        #[derive(Debug)]
        pub struct $name;

        #[cfg(feature = $feature)]
        impl<T> Loader<T> for $name
        where
            T: for<'de> serde::Deserialize<'de>,
        {
            #[inline]
            fn load(content: Vec<u8>) -> Result<T, Box<dyn Error + Send + Sync>> {
                Ok($fun(&*content)?)
            }
        }

    }
}

serde_loader!("bincode", "Loads assets from Bincode encoded files.", BincodeLoader, serde_bincode::deserialize);
serde_loader!("cbor", "Loads assets from CBOR encoded files.", CborLoader, serde_cbor::from_slice);
serde_loader!("json", "Loads assets from JSON files.", JsonLoader, serde_json::from_slice);
serde_loader!("msgpack", "Loads assets from MessagePack files.", MessagePackLoader, serde_msgpack::decode::from_read);
serde_loader!("ron", "Loads assets from RON files.", RonLoader, serde_ron::de::from_bytes);
serde_loader!("toml", "Loads assets from TOML files.", TomlLoader, serde_toml::de::from_slice);
serde_loader!("yaml", "Loads assets from YAML files.", YamlLoader, serde_yaml::from_slice);