rend3_framework/
assets.rs

1use std::borrow::Cow;
2
3use rend3::util::typedefs::SsoString;
4use thiserror::Error;
5
6#[derive(Debug, Error)]
7pub enum AssetError {
8    #[error("Could not read {path} from disk")]
9    #[cfg(not(target_arch = "wasm32"))]
10    FileError {
11        path: SsoString,
12        #[source]
13        error: std::io::Error,
14    },
15    #[error("Could not read {path} from the network")]
16    #[cfg(target_arch = "wasm32")]
17    NetworkError {
18        path: SsoString,
19        #[source]
20        error: reqwest::Error,
21    },
22    #[error("Reading {path} from the network returned non-success status code {status}")]
23    #[cfg(target_arch = "wasm32")]
24    NetworkStatusError {
25        path: SsoString,
26        status: reqwest::StatusCode,
27    },
28}
29
30pub enum AssetPath<'a> {
31    Internal(&'a str),
32    External(&'a str),
33}
34impl<'a> AssetPath<'a> {
35    fn get_path(self, base: &str) -> Cow<'a, str> {
36        match self {
37            Self::Internal(p) => Cow::Owned(base.to_owned() + p),
38            Self::External(p) => Cow::Borrowed(p),
39        }
40    }
41}
42
43pub struct AssetLoader {
44    base: SsoString,
45}
46impl AssetLoader {
47    pub fn new_local(_base_file: &str, _base_asset: &str, _base_url: &str) -> Self {
48        cfg_if::cfg_if!(
49            if #[cfg(target_arch = "wasm32")] {
50                let base = _base_url;
51            } else if #[cfg(target_os = "android")] {
52                let base = _base_asset;
53            } else {
54                let base = _base_file;
55            }
56        );
57
58        Self {
59            base: SsoString::from(base),
60        }
61    }
62
63    pub fn get_asset_path<'a>(&self, path: AssetPath<'a>) -> Cow<'a, str> {
64        path.get_path(&self.base)
65    }
66
67    #[cfg(all(not(target_arch = "wasm32"), not(target_os = "android")))]
68    pub async fn get_asset(&self, path: AssetPath<'_>) -> Result<Vec<u8>, AssetError> {
69        let full_path = path.get_path(&self.base);
70        std::fs::read(&*full_path).map_err(|error| AssetError::FileError {
71            path: SsoString::from(full_path),
72            error,
73        })
74    }
75
76    #[cfg(target_os = "android")]
77    pub async fn get_asset(&self, path: AssetPath<'_>) -> Result<Vec<u8>, AssetError> {
78        use std::ffi::CString;
79
80        let manager = ndk_glue::native_activity().asset_manager();
81
82        let full_path = path.get_path(&self.base);
83        manager
84            .open(&CString::new(&*full_path).unwrap())
85            .ok_or_else(|| AssetError::FileError {
86                path: SsoString::from(&*full_path),
87                error: std::io::Error::new(std::io::ErrorKind::NotFound, "could not find file in asset manager"),
88            })
89            .and_then(|mut file| {
90                file.get_buffer()
91                    .map(|b| b.to_vec())
92                    .map_err(|error| AssetError::FileError {
93                        path: SsoString::from(full_path),
94                        error,
95                    })
96            })
97    }
98
99    #[cfg(target_arch = "wasm32")]
100    pub async fn get_asset(&self, path: AssetPath<'_>) -> Result<Vec<u8>, AssetError> {
101        let full_path = path.get_path(&self.base);
102        let response = reqwest::get(&*full_path)
103            .await
104            .map_err(|error| AssetError::NetworkError {
105                path: SsoString::from(&*full_path),
106                error,
107            })?;
108
109        let status = response.status();
110        if !status.is_success() {
111            return Err(AssetError::NetworkStatusError {
112                path: SsoString::from(&*full_path),
113                status,
114            });
115        }
116
117        Ok(response
118            .bytes()
119            .await
120            .map_err(|error| AssetError::NetworkError {
121                path: SsoString::from(&*full_path),
122                error,
123            })?
124            .to_vec())
125    }
126}