1use crate::prelude::*;
4
5#[derive(Clone, Debug, Eq, PartialEq, Reflect)]
7pub enum Storage {
8 #[cfg(not(target_family = "wasm"))]
9 Filesystem { path: PathBuf },
10 #[cfg(target_family = "wasm")]
11 LocalStorage { key: String },
12 #[cfg(target_family = "wasm")]
13 SessionStorage { key: String },
14}
15
16impl Storage {
17 pub fn initialize(&self) -> Result<(), PersistenceError> {
19 match self {
20 #[cfg(not(target_family = "wasm"))]
21 Storage::Filesystem { path } => {
22 if let Some(parent) = path.parent() {
23 std::fs::create_dir_all(parent)?;
24 }
25 },
26 #[cfg(target_family = "wasm")]
27 Storage::LocalStorage { .. } => {},
28 #[cfg(target_family = "wasm")]
29 Storage::SessionStorage { .. } => {},
30 }
31 Ok(())
32 }
33
34 pub fn occupied(&self) -> bool {
36 match self {
37 #[cfg(not(target_family = "wasm"))]
38 Storage::Filesystem { path } => path.exists(),
39 #[cfg(target_family = "wasm")]
40 Storage::LocalStorage { key } => {
41 use gloo_storage::{
42 LocalStorage,
43 Storage,
44 };
45 matches!(LocalStorage::raw().get_item(key), Ok(Some(_)))
46 },
47 #[cfg(target_family = "wasm")]
48 Storage::SessionStorage { key } => {
49 use gloo_storage::{
50 SessionStorage,
51 Storage,
52 };
53 matches!(SessionStorage::raw().get_item(key), Ok(Some(_)))
54 },
55 }
56 }
57
58 pub fn read<R: Serialize + DeserializeOwned>(
60 &self,
61 name: &str,
62 format: StorageFormat,
63 ) -> Result<R, PersistenceError> {
64 match self {
65 #[cfg(not(target_family = "wasm"))]
66 Storage::Filesystem { path } => {
67 let bytes = std::fs::read(path)?;
68 format.deserialize::<R>(name, &bytes)
69 },
70 #[cfg(target_family = "wasm")]
71 Storage::LocalStorage { key } => {
72 use gloo_storage::{
73 LocalStorage,
74 Storage,
75 errors::StorageError,
76 };
77
78 #[cfg(feature = "json")]
79 if format == StorageFormat::Json {
80 return Ok(LocalStorage::get::<R>(key).inspect_err(|error| {
81 if let StorageError::SerdeError(error) = &error {
82 log::error!("failed to parse {} as JSON\n\n{}", name, error);
83 }
84 })?);
85 }
86 #[cfg(all(feature = "json", feature = "pretty"))]
87 if format == StorageFormat::JsonPretty {
88 return Ok(LocalStorage::get::<R>(key).inspect_err(|error| {
89 if let StorageError::SerdeError(error) = &error {
90 log::error!("failed to parse {} as pretty JSON\n\n{}", name, error);
91 }
92 })?);
93 }
94
95 #[cfg(feature = "bincode")]
96 if format == StorageFormat::Bincode {
97 let bytes = LocalStorage::get::<Vec<u8>>(key).inspect_err(|error| {
98 if let StorageError::SerdeError(error) = &error {
99 log::error!("failed to get {} as a byte array\n\n{}", name, error);
100 }
101 })?;
102 return format.deserialize::<R>(name, &bytes);
103 }
104
105 let content = LocalStorage::get::<String>(key).inspect_err(|error| {
106 if let StorageError::SerdeError(error) = &error {
107 log::error!("failed to get {} as a string\n\n{}", name, error);
108 }
109 })?;
110 format.deserialize::<R>(name, content.as_bytes())
111 },
112 #[cfg(target_family = "wasm")]
113 Storage::SessionStorage { key } => {
114 use gloo_storage::{
115 SessionStorage,
116 Storage,
117 errors::StorageError,
118 };
119
120 #[cfg(feature = "json")]
121 if format == StorageFormat::Json {
122 return Ok(SessionStorage::get::<R>(key).inspect_err(|error| {
123 if let StorageError::SerdeError(error) = &error {
124 log::error!("failed to parse {} as JSON\n\n{}", name, error);
125 }
126 })?);
127 }
128 #[cfg(all(feature = "json", feature = "pretty"))]
129 if format == StorageFormat::JsonPretty {
130 return Ok(SessionStorage::get::<R>(key).inspect_err(|error| {
131 if let StorageError::SerdeError(error) = &error {
132 log::error!("failed to parse {} as pretty JSON\n\n{}", name, error);
133 }
134 })?);
135 }
136
137 #[cfg(feature = "bincode")]
138 if format == StorageFormat::Bincode {
139 let bytes = SessionStorage::get::<Vec<u8>>(key).inspect_err(|error| {
140 if let StorageError::SerdeError(error) = &error {
141 log::error!("failed to get {} as a byte array\n\n{}", name, error);
142 }
143 })?;
144 return format.deserialize::<R>(name, &bytes);
145 }
146
147 let content = SessionStorage::get::<String>(key).inspect_err(|error| {
148 if let StorageError::SerdeError(error) = &error {
149 log::error!("failed to get {} as a string\n\n{}", name, error);
150 }
151 })?;
152 format.deserialize::<R>(name, content.as_bytes())
153 },
154 }
155 }
156
157 pub fn write<R: Serialize + DeserializeOwned>(
159 &self,
160 name: &str,
161 format: StorageFormat,
162 resource: &R,
163 ) -> Result<(), PersistenceError> {
164 match self {
165 #[cfg(not(target_family = "wasm"))]
166 Storage::Filesystem { path } => {
167 let bytes = format.serialize(name, resource)?;
168
169 use std::io::Write;
170 std::fs::OpenOptions::new()
171 .create(true)
172 .truncate(true)
173 .write(true)
174 .open(path)
175 .and_then(|mut file| file.write_all(&bytes))?;
176 },
177 #[cfg(target_family = "wasm")]
178 Storage::LocalStorage { key } => {
179 use gloo_storage::{
180 LocalStorage,
181 Storage,
182 errors::StorageError,
183 };
184
185 #[cfg(feature = "json")]
186 if format == StorageFormat::Json {
187 LocalStorage::set::<&R>(key, resource).inspect_err(|error| {
188 if let StorageError::SerdeError(error) = &error {
189 log::error!("failed to serialize {} to JSON\n\n{}", name, error);
190 }
191 })?;
192 return Ok(());
193 }
194 #[cfg(all(feature = "json", feature = "pretty"))]
195 if format == StorageFormat::JsonPretty {
196 LocalStorage::set::<&R>(key, resource).inspect_err(|error| {
197 if let StorageError::SerdeError(error) = &error {
198 log::error!("failed to serialize {} to pretty JSON\n\n{}", name, error);
199 }
200 })?;
201 return Ok(());
202 }
203
204 #[cfg(feature = "bincode")]
205 if format == StorageFormat::Bincode {
206 let bytes = format.serialize(name, resource)?;
207 LocalStorage::set::<&[u8]>(key, &bytes)?;
208 return Ok(());
209 }
210
211 let bytes = format.serialize(name, resource)?;
212
213 let string = std::str::from_utf8(&bytes).unwrap();
217 LocalStorage::set::<&str>(key, string)?;
218 },
219 #[cfg(target_family = "wasm")]
220 Storage::SessionStorage { key } => {
221 use gloo_storage::{
222 SessionStorage,
223 Storage,
224 errors::StorageError,
225 };
226
227 #[cfg(feature = "json")]
228 if format == StorageFormat::Json {
229 SessionStorage::set::<&R>(key, resource).inspect_err(|error| {
230 if let StorageError::SerdeError(error) = &error {
231 log::error!("failed to serialize {} to JSON\n\n{}", name, error);
232 }
233 })?;
234 return Ok(());
235 }
236 #[cfg(all(feature = "json", feature = "pretty"))]
237 if format == StorageFormat::JsonPretty {
238 SessionStorage::set::<&R>(key, resource).inspect_err(|error| {
239 if let StorageError::SerdeError(error) = &error {
240 log::error!("failed to serialize {} to pretty JSON\n\n{}", name, error);
241 }
242 })?;
243 return Ok(());
244 }
245
246 #[cfg(feature = "bincode")]
247 if format == StorageFormat::Bincode {
248 let bytes = format.serialize(name, resource)?;
249 SessionStorage::set::<&[u8]>(key, &bytes)?;
250 return Ok(());
251 }
252
253 let bytes = format.serialize(name, resource)?;
254
255 let string = std::str::from_utf8(&bytes).unwrap();
259 SessionStorage::set::<&str>(key, string)?;
260 },
261 }
262 Ok(())
263 }
264}
265
266impl Display for Storage {
267 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
268 match self {
269 #[cfg(not(target_family = "wasm"))]
270 Storage::Filesystem { path } => {
271 if let Some(path) = path.to_str() {
272 write!(f, "{path}")
273 } else {
274 write!(f, "{path:?}")
275 }
276 },
277 #[cfg(target_family = "wasm")]
278 Storage::LocalStorage { key } => {
279 let separator = std::path::MAIN_SEPARATOR;
280 write!(f, "{separator}local{separator}{key}")
281 },
282 #[cfg(target_family = "wasm")]
283 Storage::SessionStorage { key } => {
284 let separator = std::path::MAIN_SEPARATOR;
285 write!(f, "{separator}session{separator}{key}")
286 },
287 }
288 }
289}