Skip to main content

thedes_asset/
lib.rs

1use std::{borrow::Cow, io, ops::Deref, path::PathBuf};
2
3use thiserror::Error;
4use tokio::sync::OnceCell;
5
6#[derive(Debug, Error)]
7#[error("Failed to load asset from {}", path.display())]
8pub struct LoadError {
9    pub path: PathBuf,
10    pub source: io::Error,
11}
12
13#[cfg(feature = "runtime-load")]
14#[doc(hidden)]
15pub async fn runtime_load(
16    path: impl AsRef<std::path::Path>,
17) -> Result<Asset, LoadError> {
18    let mut buf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
19    buf.push(path.as_ref());
20    let bytes = tokio::fs::read(&buf)
21        .await
22        .map_err(|source| LoadError { path: buf.clone(), source })?;
23    Ok(Asset::from_buf(bytes))
24}
25
26#[cfg(feature = "runtime-load")]
27macro_rules! load {
28    ($path:expr) => {
29        $crate::runtime_load($path)
30    };
31}
32
33#[cfg(not(feature = "runtime-load"))]
34macro_rules! load {
35    ($path:expr) => {
36        async move {
37            let bytes =
38                include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/", $path));
39            let asset = $crate::Asset::from_static(bytes);
40            Result::<_, LoadError>::Ok(asset)
41        }
42    };
43}
44
45#[derive(Debug, Clone)]
46pub struct Asset {
47    bytes: Cow<'static, [u8]>,
48}
49
50impl Asset {
51    pub fn from_static(bytes: &'static [u8]) -> Self {
52        Self { bytes: bytes.into() }
53    }
54
55    pub fn from_buf(bytes: Vec<u8>) -> Self {
56        Self { bytes: bytes.into() }
57    }
58
59    pub fn as_bytes(&self) -> &[u8] {
60        &self.bytes
61    }
62}
63
64impl Deref for Asset {
65    type Target = [u8];
66
67    fn deref(&self) -> &Self::Target {
68        self.as_bytes()
69    }
70}
71
72#[derive(Debug)]
73#[non_exhaustive]
74pub struct SoundAssets {
75    pub main_theme: Asset,
76    pub calm_song: Asset,
77    pub hit: Asset,
78    pub growl: Asset,
79}
80
81impl SoundAssets {
82    async fn load() -> Result<Self, LoadError> {
83        Ok(Self {
84            main_theme: load!("../assets/audio/thedes-theme.ogg").await?,
85            calm_song: load!("../assets/audio/calm-song.ogg").await?,
86            hit: load!("../assets/audio/hit.ogg").await?,
87            growl: load!("../assets/audio/growl.ogg").await?,
88        })
89    }
90}
91
92#[derive(Debug)]
93#[non_exhaustive]
94pub struct Assets {
95    pub sound: SoundAssets,
96}
97
98impl Assets {
99    pub async fn get() -> Result<&'static Self, LoadError> {
100        static SELF: OnceCell<Assets> = OnceCell::const_new();
101        SELF.get_or_try_init(Self::load).await
102    }
103
104    async fn load() -> Result<Self, LoadError> {
105        Ok(Self { sound: SoundAssets::load().await? })
106    }
107}