rend3_framework/
assets.rs1use 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}