1use serde::{Serialize, Deserialize};
18use std::collections::HashMap;
19use std::sync::{Arc, RwLock};
20
21#[derive(Debug, thiserror::Error)]
27pub enum StorageError {
28 #[error("Key not found: {0}")]
29 KeyNotFound(String),
30
31 #[error("Storage backend not available: {0}")]
32 BackendUnavailable(String),
33
34 #[error("Secure storage not supported on this platform")]
35 SecureNotSupported,
36
37 #[error("File not found: {0}")]
38 FileNotFound(String),
39
40 #[error("Permission denied: {0}")]
41 PermissionDenied(String),
42
43 #[error("Serialization error: {0}")]
44 SerializationError(String),
45
46 #[error("Storage quota exceeded")]
47 QuotaExceeded,
48
49 #[error("Storage I/O error: {0}")]
50 IoError(String),
51}
52
53pub type StorageResult<T> = Result<T, StorageError>;
54
55#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
61#[serde(untagged)]
62pub enum StorageValue {
63 Null,
64 Bool(bool),
65 Int(i64),
66 Float(f64),
67 String(String),
68 Bytes(Vec<u8>),
69 Array(Vec<StorageValue>),
70 Map(HashMap<String, StorageValue>),
71}
72
73impl StorageValue {
74 pub fn as_str(&self) -> Option<&str> {
75 match self {
76 StorageValue::String(s) => Some(s),
77 _ => None,
78 }
79 }
80
81 pub fn as_bool(&self) -> Option<bool> {
82 match self {
83 StorageValue::Bool(b) => Some(*b),
84 _ => None,
85 }
86 }
87
88 pub fn as_i64(&self) -> Option<i64> {
89 match self {
90 StorageValue::Int(i) => Some(*i),
91 _ => None,
92 }
93 }
94
95 pub fn as_f64(&self) -> Option<f64> {
96 match self {
97 StorageValue::Float(f) => Some(*f),
98 _ => None,
99 }
100 }
101
102 pub fn is_null(&self) -> bool {
103 matches!(self, StorageValue::Null)
104 }
105}
106
107impl From<String> for StorageValue {
108 fn from(s: String) -> Self { StorageValue::String(s) }
109}
110
111impl From<&str> for StorageValue {
112 fn from(s: &str) -> Self { StorageValue::String(s.to_string()) }
113}
114
115impl From<bool> for StorageValue {
116 fn from(b: bool) -> Self { StorageValue::Bool(b) }
117}
118
119impl From<i64> for StorageValue {
120 fn from(i: i64) -> Self { StorageValue::Int(i) }
121}
122
123impl From<f64> for StorageValue {
124 fn from(f: f64) -> Self { StorageValue::Float(f) }
125}
126
127#[derive(Debug, Clone, PartialEq, Eq, Hash)]
134pub struct StorageNamespace(String);
135
136impl StorageNamespace {
137 pub fn new(name: impl Into<String>) -> Self {
138 Self(name.into())
139 }
140
141 pub fn app_data() -> Self { Self("app_data".to_string()) }
143
144 pub fn preferences() -> Self { Self("preferences".to_string()) }
146
147 pub fn cache() -> Self { Self("cache".to_string()) }
149
150 pub fn as_str(&self) -> &str { &self.0 }
151
152 pub fn prefixed_key(&self, key: &str) -> String {
154 format!("{}:{}", self.0, key)
155 }
156}
157
158impl Default for StorageNamespace {
159 fn default() -> Self { Self::app_data() }
160}
161
162pub trait StorageBackend: Send + Sync {
173 fn platform_id(&self) -> &str;
175
176 fn get(&self, namespace: &StorageNamespace, key: &str) -> StorageResult<Option<StorageValue>>;
180
181 fn set(&self, namespace: &StorageNamespace, key: &str, value: StorageValue) -> StorageResult<()>;
183
184 fn delete(&self, namespace: &StorageNamespace, key: &str) -> StorageResult<()>;
186
187 fn contains(&self, namespace: &StorageNamespace, key: &str) -> StorageResult<bool> {
189 Ok(self.get(namespace, key)?.is_some())
190 }
191
192 fn keys(&self, namespace: &StorageNamespace) -> StorageResult<Vec<String>>;
194
195 fn clear(&self, namespace: &StorageNamespace) -> StorageResult<()>;
197
198 fn supports_secure(&self) -> bool { false }
202
203 fn secure_set(&self, _key: &str, _value: &[u8]) -> StorageResult<()> {
205 Err(StorageError::SecureNotSupported)
206 }
207
208 fn secure_get(&self, _key: &str) -> StorageResult<Option<Vec<u8>>> {
210 Err(StorageError::SecureNotSupported)
211 }
212
213 fn secure_delete(&self, _key: &str) -> StorageResult<()> {
215 Err(StorageError::SecureNotSupported)
216 }
217
218 fn write_file(&self, path: &str, data: &[u8]) -> StorageResult<()>;
222
223 fn read_file(&self, path: &str) -> StorageResult<Vec<u8>>;
225
226 fn delete_file(&self, path: &str) -> StorageResult<()>;
228
229 fn file_exists(&self, path: &str) -> StorageResult<bool>;
231
232 fn list_files(&self, dir: &str) -> StorageResult<Vec<String>>;
234}
235
236pub struct StorageManager {
243 backend: Arc<dyn StorageBackend>,
244}
245
246impl StorageManager {
247 pub fn new(backend: Arc<dyn StorageBackend>) -> Self {
248 Self { backend }
249 }
250
251 pub fn platform_id(&self) -> &str {
252 self.backend.platform_id()
253 }
254
255 pub fn get_string(&self, ns: &StorageNamespace, key: &str) -> StorageResult<Option<String>> {
259 match self.backend.get(ns, key)? {
260 Some(StorageValue::String(s)) => Ok(Some(s)),
261 Some(_) => Ok(None),
262 None => Ok(None),
263 }
264 }
265
266 pub fn set_string(&self, ns: &StorageNamespace, key: &str, value: &str) -> StorageResult<()> {
268 self.backend.set(ns, key, StorageValue::String(value.to_string()))
269 }
270
271 pub fn get_bool(&self, ns: &StorageNamespace, key: &str) -> StorageResult<Option<bool>> {
273 match self.backend.get(ns, key)? {
274 Some(StorageValue::Bool(b)) => Ok(Some(b)),
275 Some(_) => Ok(None),
276 None => Ok(None),
277 }
278 }
279
280 pub fn get_json<T: serde::de::DeserializeOwned>(
282 &self, ns: &StorageNamespace, key: &str,
283 ) -> StorageResult<Option<T>> {
284 match self.backend.get(ns, key)? {
285 Some(StorageValue::String(s)) => {
286 let val = serde_json::from_str(&s)
287 .map_err(|e| StorageError::SerializationError(e.to_string()))?;
288 Ok(Some(val))
289 }
290 _ => Ok(None),
291 }
292 }
293
294 pub fn set_json<T: Serialize>(
296 &self, ns: &StorageNamespace, key: &str, value: &T,
297 ) -> StorageResult<()> {
298 let json = serde_json::to_string(value)
299 .map_err(|e| StorageError::SerializationError(e.to_string()))?;
300 self.backend.set(ns, key, StorageValue::String(json))
301 }
302
303 pub fn multi_get(
305 &self, ns: &StorageNamespace, keys: &[&str],
306 ) -> StorageResult<HashMap<String, StorageValue>> {
307 let mut result = HashMap::new();
308 for &key in keys {
309 if let Some(val) = self.backend.get(ns, key)? {
310 result.insert(key.to_string(), val);
311 }
312 }
313 Ok(result)
314 }
315
316 pub fn multi_set(
318 &self, ns: &StorageNamespace, entries: &[(&str, StorageValue)],
319 ) -> StorageResult<()> {
320 for (key, value) in entries {
321 self.backend.set(ns, key, value.clone())?;
322 }
323 Ok(())
324 }
325
326 pub fn supports_secure(&self) -> bool {
329 self.backend.supports_secure()
330 }
331
332 pub fn secure_set_string(&self, key: &str, value: &str) -> StorageResult<()> {
334 self.backend.secure_set(key, value.as_bytes())
335 }
336
337 pub fn secure_get_string(&self, key: &str) -> StorageResult<Option<String>> {
339 match self.backend.secure_get(key)? {
340 Some(bytes) => {
341 let s = String::from_utf8(bytes)
342 .map_err(|e| StorageError::SerializationError(e.to_string()))?;
343 Ok(Some(s))
344 }
345 None => Ok(None),
346 }
347 }
348
349 pub fn write_text(&self, path: &str, text: &str) -> StorageResult<()> {
353 self.backend.write_file(path, text.as_bytes())
354 }
355
356 pub fn read_text(&self, path: &str) -> StorageResult<String> {
358 let bytes = self.backend.read_file(path)?;
359 String::from_utf8(bytes)
360 .map_err(|e| StorageError::SerializationError(e.to_string()))
361 }
362}
363
364pub struct MemoryStorageBackend {
371 platform: String,
372 kv: RwLock<HashMap<String, StorageValue>>,
373 secure: RwLock<HashMap<String, Vec<u8>>>,
374 files: RwLock<HashMap<String, Vec<u8>>>,
375}
376
377impl MemoryStorageBackend {
378 pub fn new(platform: impl Into<String>) -> Self {
379 Self {
380 platform: platform.into(),
381 kv: RwLock::new(HashMap::new()),
382 secure: RwLock::new(HashMap::new()),
383 files: RwLock::new(HashMap::new()),
384 }
385 }
386}
387
388impl StorageBackend for MemoryStorageBackend {
389 fn platform_id(&self) -> &str { &self.platform }
390
391 fn get(&self, namespace: &StorageNamespace, key: &str) -> StorageResult<Option<StorageValue>> {
392 let full_key = namespace.prefixed_key(key);
393 let store = self.kv.read().unwrap();
394 Ok(store.get(&full_key).cloned())
395 }
396
397 fn set(&self, namespace: &StorageNamespace, key: &str, value: StorageValue) -> StorageResult<()> {
398 let full_key = namespace.prefixed_key(key);
399 let mut store = self.kv.write().unwrap();
400 store.insert(full_key, value);
401 Ok(())
402 }
403
404 fn delete(&self, namespace: &StorageNamespace, key: &str) -> StorageResult<()> {
405 let full_key = namespace.prefixed_key(key);
406 let mut store = self.kv.write().unwrap();
407 store.remove(&full_key);
408 Ok(())
409 }
410
411 fn contains(&self, namespace: &StorageNamespace, key: &str) -> StorageResult<bool> {
412 let full_key = namespace.prefixed_key(key);
413 let store = self.kv.read().unwrap();
414 Ok(store.contains_key(&full_key))
415 }
416
417 fn keys(&self, namespace: &StorageNamespace) -> StorageResult<Vec<String>> {
418 let prefix = format!("{}:", namespace.as_str());
419 let store = self.kv.read().unwrap();
420 let keys: Vec<String> = store.keys()
421 .filter(|k| k.starts_with(&prefix))
422 .map(|k| k[prefix.len()..].to_string())
423 .collect();
424 Ok(keys)
425 }
426
427 fn clear(&self, namespace: &StorageNamespace) -> StorageResult<()> {
428 let prefix = format!("{}:", namespace.as_str());
429 let mut store = self.kv.write().unwrap();
430 store.retain(|k, _| !k.starts_with(&prefix));
431 Ok(())
432 }
433
434 fn supports_secure(&self) -> bool { true }
435
436 fn secure_set(&self, key: &str, value: &[u8]) -> StorageResult<()> {
437 let mut secure = self.secure.write().unwrap();
438 secure.insert(key.to_string(), value.to_vec());
439 Ok(())
440 }
441
442 fn secure_get(&self, key: &str) -> StorageResult<Option<Vec<u8>>> {
443 let secure = self.secure.read().unwrap();
444 Ok(secure.get(key).cloned())
445 }
446
447 fn secure_delete(&self, key: &str) -> StorageResult<()> {
448 let mut secure = self.secure.write().unwrap();
449 secure.remove(key);
450 Ok(())
451 }
452
453 fn write_file(&self, path: &str, data: &[u8]) -> StorageResult<()> {
454 let mut files = self.files.write().unwrap();
455 files.insert(path.to_string(), data.to_vec());
456 Ok(())
457 }
458
459 fn read_file(&self, path: &str) -> StorageResult<Vec<u8>> {
460 let files = self.files.read().unwrap();
461 files.get(path).cloned()
462 .ok_or_else(|| StorageError::FileNotFound(path.to_string()))
463 }
464
465 fn delete_file(&self, path: &str) -> StorageResult<()> {
466 let mut files = self.files.write().unwrap();
467 files.remove(path);
468 Ok(())
469 }
470
471 fn file_exists(&self, path: &str) -> StorageResult<bool> {
472 let files = self.files.read().unwrap();
473 Ok(files.contains_key(path))
474 }
475
476 fn list_files(&self, dir: &str) -> StorageResult<Vec<String>> {
477 let prefix = if dir.ends_with('/') { dir.to_string() } else { format!("{dir}/") };
478 let files = self.files.read().unwrap();
479 let listing: Vec<String> = files.keys()
480 .filter(|k| k.starts_with(&prefix))
481 .cloned()
482 .collect();
483 Ok(listing)
484 }
485}
486
487pub struct IosStorageBackend {
494 memory: MemoryStorageBackend,
495}
496
497impl IosStorageBackend {
498 pub fn new() -> Self {
499 Self { memory: MemoryStorageBackend::new("ios") }
500 }
501}
502
503impl StorageBackend for IosStorageBackend {
504 fn platform_id(&self) -> &str { "ios" }
505 fn get(&self, ns: &StorageNamespace, key: &str) -> StorageResult<Option<StorageValue>> { self.memory.get(ns, key) }
506 fn set(&self, ns: &StorageNamespace, key: &str, value: StorageValue) -> StorageResult<()> { self.memory.set(ns, key, value) }
507 fn delete(&self, ns: &StorageNamespace, key: &str) -> StorageResult<()> { self.memory.delete(ns, key) }
508 fn keys(&self, ns: &StorageNamespace) -> StorageResult<Vec<String>> { self.memory.keys(ns) }
509 fn clear(&self, ns: &StorageNamespace) -> StorageResult<()> { self.memory.clear(ns) }
510 fn supports_secure(&self) -> bool { true } fn secure_set(&self, key: &str, value: &[u8]) -> StorageResult<()> { self.memory.secure_set(key, value) }
512 fn secure_get(&self, key: &str) -> StorageResult<Option<Vec<u8>>> { self.memory.secure_get(key) }
513 fn secure_delete(&self, key: &str) -> StorageResult<()> { self.memory.secure_delete(key) }
514 fn write_file(&self, path: &str, data: &[u8]) -> StorageResult<()> { self.memory.write_file(path, data) }
515 fn read_file(&self, path: &str) -> StorageResult<Vec<u8>> { self.memory.read_file(path) }
516 fn delete_file(&self, path: &str) -> StorageResult<()> { self.memory.delete_file(path) }
517 fn file_exists(&self, path: &str) -> StorageResult<bool> { self.memory.file_exists(path) }
518 fn list_files(&self, dir: &str) -> StorageResult<Vec<String>> { self.memory.list_files(dir) }
519}
520
521pub struct AndroidStorageBackend {
524 memory: MemoryStorageBackend,
525}
526
527impl AndroidStorageBackend {
528 pub fn new() -> Self {
529 Self { memory: MemoryStorageBackend::new("android") }
530 }
531}
532
533impl StorageBackend for AndroidStorageBackend {
534 fn platform_id(&self) -> &str { "android" }
535 fn get(&self, ns: &StorageNamespace, key: &str) -> StorageResult<Option<StorageValue>> { self.memory.get(ns, key) }
536 fn set(&self, ns: &StorageNamespace, key: &str, value: StorageValue) -> StorageResult<()> { self.memory.set(ns, key, value) }
537 fn delete(&self, ns: &StorageNamespace, key: &str) -> StorageResult<()> { self.memory.delete(ns, key) }
538 fn keys(&self, ns: &StorageNamespace) -> StorageResult<Vec<String>> { self.memory.keys(ns) }
539 fn clear(&self, ns: &StorageNamespace) -> StorageResult<()> { self.memory.clear(ns) }
540 fn supports_secure(&self) -> bool { true } fn secure_set(&self, key: &str, value: &[u8]) -> StorageResult<()> { self.memory.secure_set(key, value) }
542 fn secure_get(&self, key: &str) -> StorageResult<Option<Vec<u8>>> { self.memory.secure_get(key) }
543 fn secure_delete(&self, key: &str) -> StorageResult<()> { self.memory.secure_delete(key) }
544 fn write_file(&self, path: &str, data: &[u8]) -> StorageResult<()> { self.memory.write_file(path, data) }
545 fn read_file(&self, path: &str) -> StorageResult<Vec<u8>> { self.memory.read_file(path) }
546 fn delete_file(&self, path: &str) -> StorageResult<()> { self.memory.delete_file(path) }
547 fn file_exists(&self, path: &str) -> StorageResult<bool> { self.memory.file_exists(path) }
548 fn list_files(&self, dir: &str) -> StorageResult<Vec<String>> { self.memory.list_files(dir) }
549}
550
551pub struct WebStorageBackend {
554 memory: MemoryStorageBackend,
555}
556
557impl WebStorageBackend {
558 pub fn new() -> Self {
559 Self { memory: MemoryStorageBackend::new("web") }
560 }
561}
562
563impl StorageBackend for WebStorageBackend {
564 fn platform_id(&self) -> &str { "web" }
565 fn get(&self, ns: &StorageNamespace, key: &str) -> StorageResult<Option<StorageValue>> { self.memory.get(ns, key) }
566 fn set(&self, ns: &StorageNamespace, key: &str, value: StorageValue) -> StorageResult<()> { self.memory.set(ns, key, value) }
567 fn delete(&self, ns: &StorageNamespace, key: &str) -> StorageResult<()> { self.memory.delete(ns, key) }
568 fn keys(&self, ns: &StorageNamespace) -> StorageResult<Vec<String>> { self.memory.keys(ns) }
569 fn clear(&self, ns: &StorageNamespace) -> StorageResult<()> { self.memory.clear(ns) }
570 fn supports_secure(&self) -> bool { false } fn write_file(&self, path: &str, data: &[u8]) -> StorageResult<()> { self.memory.write_file(path, data) }
572 fn read_file(&self, path: &str) -> StorageResult<Vec<u8>> { self.memory.read_file(path) }
573 fn delete_file(&self, path: &str) -> StorageResult<()> { self.memory.delete_file(path) }
574 fn file_exists(&self, path: &str) -> StorageResult<bool> { self.memory.file_exists(path) }
575 fn list_files(&self, dir: &str) -> StorageResult<Vec<String>> { self.memory.list_files(dir) }
576}
577
578pub struct DesktopStorageBackend {
581 memory: MemoryStorageBackend,
582}
583
584impl DesktopStorageBackend {
585 pub fn new() -> Self {
586 Self { memory: MemoryStorageBackend::new("desktop") }
587 }
588}
589
590impl StorageBackend for DesktopStorageBackend {
591 fn platform_id(&self) -> &str { "desktop" }
592 fn get(&self, ns: &StorageNamespace, key: &str) -> StorageResult<Option<StorageValue>> { self.memory.get(ns, key) }
593 fn set(&self, ns: &StorageNamespace, key: &str, value: StorageValue) -> StorageResult<()> { self.memory.set(ns, key, value) }
594 fn delete(&self, ns: &StorageNamespace, key: &str) -> StorageResult<()> { self.memory.delete(ns, key) }
595 fn keys(&self, ns: &StorageNamespace) -> StorageResult<Vec<String>> { self.memory.keys(ns) }
596 fn clear(&self, ns: &StorageNamespace) -> StorageResult<()> { self.memory.clear(ns) }
597 fn supports_secure(&self) -> bool { true } fn secure_set(&self, key: &str, value: &[u8]) -> StorageResult<()> { self.memory.secure_set(key, value) }
599 fn secure_get(&self, key: &str) -> StorageResult<Option<Vec<u8>>> { self.memory.secure_get(key) }
600 fn secure_delete(&self, key: &str) -> StorageResult<()> { self.memory.secure_delete(key) }
601 fn write_file(&self, path: &str, data: &[u8]) -> StorageResult<()> { self.memory.write_file(path, data) }
602 fn read_file(&self, path: &str) -> StorageResult<Vec<u8>> { self.memory.read_file(path) }
603 fn delete_file(&self, path: &str) -> StorageResult<()> { self.memory.delete_file(path) }
604 fn file_exists(&self, path: &str) -> StorageResult<bool> { self.memory.file_exists(path) }
605 fn list_files(&self, dir: &str) -> StorageResult<Vec<String>> { self.memory.list_files(dir) }
606}
607
608#[cfg(test)]
613mod tests {
614 use super::*;
615
616 fn make_manager() -> StorageManager {
617 StorageManager::new(Arc::new(MemoryStorageBackend::new("test")))
618 }
619
620 #[test]
621 fn kv_set_get_delete() {
622 let mgr = make_manager();
623 let ns = StorageNamespace::app_data();
624
625 assert!(mgr.get_string(&ns, "name").unwrap().is_none());
627
628 mgr.set_string(&ns, "name", "AppScale").unwrap();
630 assert_eq!(mgr.get_string(&ns, "name").unwrap().unwrap(), "AppScale");
631
632 mgr.backend.delete(&ns, "name").unwrap();
634 assert!(mgr.get_string(&ns, "name").unwrap().is_none());
635 }
636
637 #[test]
638 fn kv_namespaces_isolated() {
639 let mgr = make_manager();
640 let ns1 = StorageNamespace::app_data();
641 let ns2 = StorageNamespace::preferences();
642
643 mgr.set_string(&ns1, "key", "value1").unwrap();
644 mgr.set_string(&ns2, "key", "value2").unwrap();
645
646 assert_eq!(mgr.get_string(&ns1, "key").unwrap().unwrap(), "value1");
647 assert_eq!(mgr.get_string(&ns2, "key").unwrap().unwrap(), "value2");
648 }
649
650 #[test]
651 fn kv_clear_namespace() {
652 let mgr = make_manager();
653 let ns = StorageNamespace::cache();
654
655 mgr.set_string(&ns, "a", "1").unwrap();
656 mgr.set_string(&ns, "b", "2").unwrap();
657 mgr.backend.clear(&ns).unwrap();
658
659 assert!(mgr.get_string(&ns, "a").unwrap().is_none());
660 assert!(mgr.get_string(&ns, "b").unwrap().is_none());
661 }
662
663 #[test]
664 fn kv_list_keys() {
665 let mgr = make_manager();
666 let ns = StorageNamespace::app_data();
667
668 mgr.set_string(&ns, "alpha", "1").unwrap();
669 mgr.set_string(&ns, "beta", "2").unwrap();
670 mgr.set_string(&ns, "gamma", "3").unwrap();
671
672 let mut keys = mgr.backend.keys(&ns).unwrap();
673 keys.sort();
674 assert_eq!(keys, vec!["alpha", "beta", "gamma"]);
675 }
676
677 #[test]
678 fn kv_contains() {
679 let mgr = make_manager();
680 let ns = StorageNamespace::app_data();
681
682 assert!(!mgr.backend.contains(&ns, "missing").unwrap());
683 mgr.set_string(&ns, "present", "here").unwrap();
684 assert!(mgr.backend.contains(&ns, "present").unwrap());
685 }
686
687 #[test]
688 fn kv_json_roundtrip() {
689 let mgr = make_manager();
690 let ns = StorageNamespace::app_data();
691
692 #[derive(Debug, Serialize, Deserialize, PartialEq)]
693 struct Config { debug: bool, max_items: u32 }
694
695 let config = Config { debug: true, max_items: 50 };
696 mgr.set_json(&ns, "config", &config).unwrap();
697
698 let loaded: Config = mgr.get_json(&ns, "config").unwrap().unwrap();
699 assert_eq!(loaded, config);
700 }
701
702 #[test]
703 fn multi_get_set() {
704 let mgr = make_manager();
705 let ns = StorageNamespace::app_data();
706
707 mgr.multi_set(&ns, &[
708 ("x", StorageValue::Int(1)),
709 ("y", StorageValue::Int(2)),
710 ("z", StorageValue::Int(3)),
711 ]).unwrap();
712
713 let result = mgr.multi_get(&ns, &["x", "z", "missing"]).unwrap();
714 assert_eq!(result.len(), 2);
715 assert_eq!(result["x"], StorageValue::Int(1));
716 assert_eq!(result["z"], StorageValue::Int(3));
717 }
718
719 #[test]
720 fn secure_storage() {
721 let mgr = make_manager();
722 assert!(mgr.supports_secure());
723
724 mgr.secure_set_string("api_token", "secret123").unwrap();
725 let token = mgr.secure_get_string("api_token").unwrap().unwrap();
726 assert_eq!(token, "secret123");
727
728 mgr.backend.secure_delete("api_token").unwrap();
729 assert!(mgr.secure_get_string("api_token").unwrap().is_none());
730 }
731
732 #[test]
733 fn file_operations() {
734 let mgr = make_manager();
735
736 assert!(!mgr.backend.file_exists("docs/readme.txt").unwrap());
737
738 mgr.write_text("docs/readme.txt", "Hello, AppScale!").unwrap();
739 assert!(mgr.backend.file_exists("docs/readme.txt").unwrap());
740
741 let text = mgr.read_text("docs/readme.txt").unwrap();
742 assert_eq!(text, "Hello, AppScale!");
743
744 let files = mgr.backend.list_files("docs").unwrap();
745 assert_eq!(files, vec!["docs/readme.txt"]);
746
747 mgr.backend.delete_file("docs/readme.txt").unwrap();
748 assert!(!mgr.backend.file_exists("docs/readme.txt").unwrap());
749 }
750
751 #[test]
752 fn file_not_found() {
753 let mgr = make_manager();
754 let result = mgr.read_text("nonexistent.txt");
755 assert!(result.is_err());
756 assert!(matches!(
757 result.unwrap_err(),
758 StorageError::FileNotFound(_)
759 ));
760 }
761
762 #[test]
763 fn storage_value_conversions() {
764 let v: StorageValue = "hello".into();
765 assert_eq!(v.as_str(), Some("hello"));
766
767 let v: StorageValue = true.into();
768 assert_eq!(v.as_bool(), Some(true));
769
770 let v: StorageValue = 42i64.into();
771 assert_eq!(v.as_i64(), Some(42));
772
773 let v: StorageValue = 3.14f64.into();
774 assert_eq!(v.as_f64(), Some(3.14));
775
776 assert!(StorageValue::Null.is_null());
777 }
778
779 #[test]
780 fn platform_adapters_construct() {
781 let ios = IosStorageBackend::new();
782 assert_eq!(ios.platform_id(), "ios");
783 assert!(ios.supports_secure());
784
785 let android = AndroidStorageBackend::new();
786 assert_eq!(android.platform_id(), "android");
787 assert!(android.supports_secure());
788
789 let web = WebStorageBackend::new();
790 assert_eq!(web.platform_id(), "web");
791 assert!(!web.supports_secure());
792
793 let desktop = DesktopStorageBackend::new();
794 assert_eq!(desktop.platform_id(), "desktop");
795 assert!(desktop.supports_secure());
796 }
797
798 #[test]
799 fn namespace_prefixed_keys() {
800 let ns = StorageNamespace::new("myapp");
801 assert_eq!(ns.prefixed_key("user"), "myapp:user");
802 assert_eq!(ns.as_str(), "myapp");
803 }
804}