1use std::fmt::{Display, Formatter};
22use std::{io::Error, path::Path};
23
24#[derive(Debug)]
25pub enum FileError {
26 Io(std::io::Error),
27 Custom(String),
28}
29
30impl std::error::Error for FileError {}
31
32impl Display for FileError {
33 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
34 match self {
35 FileError::Io(err) => {
36 write!(f, "Io error: {err}")
37 }
38 FileError::Custom(err) => {
39 write!(f, "{err}")
40 }
41 }
42 }
43}
44
45impl From<std::io::Error> for FileError {
46 fn from(e: Error) -> Self {
47 Self::Io(e)
48 }
49}
50
51impl From<&str> for FileError {
52 fn from(value: &str) -> Self {
53 Self::Custom(value.to_string())
54 }
55}
56
57impl From<String> for FileError {
58 fn from(value: String) -> Self {
59 Self::Custom(value)
60 }
61}
62
63#[cfg(target_os = "android")]
64pub static ANDROID_APP: once_cell::sync::OnceCell<android_activity::AndroidApp> =
65 once_cell::sync::OnceCell::new();
66
67#[cfg(target_arch = "wasm32")]
68impl From<wasm_bindgen::JsValue> for FileError {
69 fn from(value: wasm_bindgen::JsValue) -> Self {
70 let string = match js_sys::JSON::stringify(&value) {
71 Ok(string) => String::from(string),
72 Err(_) => format!("{:?}", value),
73 };
74 Self::Custom(string)
75 }
76}
77
78pub async fn load_file<P: AsRef<Path>>(path: P) -> Result<Vec<u8>, FileError> {
79 #[cfg(all(not(target_os = "android"), not(target_arch = "wasm32")))]
80 {
81 use std::fs::File;
82 use std::io::Read;
83
84 let mut file = File::open(path)?;
85 let mut buffer = Vec::new();
86 file.read_to_end(&mut buffer)?;
87 Ok(buffer)
88 }
89
90 #[cfg(target_os = "android")]
91 {
92 let asset_manager = ANDROID_APP
93 .get()
94 .ok_or_else(|| FileError::Custom("ANDROID_APP is not set".to_string()))?
95 .asset_manager();
96 let mut opened_asset = asset_manager
97 .open(&std::ffi::CString::new(path.as_ref().to_str().unwrap()).unwrap())
98 .ok_or_else(|| FileError::Custom(format!("File {:?} not found!", path.as_ref())))?;
99 let bytes = opened_asset.buffer()?;
100 Ok(bytes.to_vec())
101 }
102
103 #[cfg(target_arch = "wasm32")]
104 {
105 use js_sys::Uint8Array;
106 use wasm_bindgen::JsCast;
107 use wasm_bindgen_futures::JsFuture;
108
109 match web_sys::window() {
110 Some(window) => {
111 let resp_value =
112 JsFuture::from(window.fetch_with_str(path.as_ref().to_str().unwrap())).await?;
113
114 let resp: web_sys::Response = resp_value.dyn_into().unwrap();
115 let data = JsFuture::from(resp.array_buffer().unwrap()).await?;
116 let bytes = Uint8Array::new(&data).to_vec();
117 Ok(bytes)
118 }
119 None => Err(FileError::Custom("Window not found!".to_owned())),
120 }
121 }
122}
123
124pub async fn exists<P: AsRef<Path>>(path: P) -> bool {
125 #[cfg(all(not(target_os = "android"), not(target_arch = "wasm32")))]
126 {
127 path.as_ref().exists()
128 }
129
130 #[cfg(target_os = "android")]
131 {
132 ANDROID_APP
133 .get()
134 .map(|v| {
135 v.asset_manager()
136 .open(&std::ffi::CString::new(path.as_ref().to_str().unwrap()).unwrap())
137 .is_some()
138 })
139 .unwrap_or_default()
140 }
141
142 #[cfg(target_arch = "wasm32")]
143 {
144 use wasm_bindgen::JsCast;
145 use wasm_bindgen_futures::JsFuture;
146
147 match web_sys::window() {
148 Some(window) => {
149 if let Ok(resp_value) =
150 JsFuture::from(window.fetch_with_str(path.as_ref().to_str().unwrap())).await
151 {
152 let resp: web_sys::Response = resp_value.dyn_into().unwrap();
153
154 resp.status() == 200
155 } else {
156 false
157 }
158 }
159 None => false,
160 }
161 }
162}
163
164pub async fn is_dir<P: AsRef<Path>>(#[allow(unused)] path: P) -> bool {
165 #[cfg(all(not(target_os = "android"), not(target_arch = "wasm32")))]
166 {
167 path.as_ref().is_dir()
168 }
169
170 #[cfg(target_os = "android")]
171 {
172 ANDROID_APP
173 .get()
174 .map(|v| {
175 v.asset_manager()
176 .open_dir(&std::ffi::CString::new(path.as_ref().to_str().unwrap()).unwrap())
177 .is_some()
178 })
179 .unwrap_or_default()
180 }
181
182 #[cfg(target_arch = "wasm32")]
184 {
185 false
186 }
187}
188
189pub async fn is_file<P: AsRef<Path>>(path: P) -> bool {
190 #[cfg(all(not(target_os = "android"), not(target_arch = "wasm32")))]
191 {
192 path.as_ref().is_file()
193 }
194
195 #[cfg(any(target_os = "android", target_arch = "wasm32"))]
197 {
198 exists(path).await
199 }
200}