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}