//! WASM-compatible storage using browser LocalStorage use crate::domain::entities::*; use crate::domain::error::DomainError; use crate::domain::traits::*; /// WASM storage using browser LocalStorage #[derive(Clone)] pub struct BrowserStorage; // Mock storage for testing on native targets #[cfg(not(target_arch = "wasm32"))] use std::collections::HashMap; #[cfg(not(target_arch = "wasm32"))] use std::sync::{Mutex, OnceLock}; #[cfg(not(target_arch = "wasm32"))] static MOCK_STORAGE: OnceLock>> = OnceLock::new(); impl FileStorage for BrowserStorage { type Error = DomainError; fn save(&self, file: &PassportFile, path: &str) -> Result<(), Self::Error> { // Real implementation for WASM targets #[cfg(target_arch = "wasm32")] { use base64::Engine; use gloo_storage::Storage; // Serialize to CBOR let data = serde_cbor::to_vec(file) .map_err(|e| DomainError::InvalidFileFormat(format!("Failed to serialize file: {}", e)))?; // Convert to base64 for storage let base64_data = base64::engine::general_purpose::STANDARD.encode(&data); // Store in browser localStorage using gloo-storage (synchronous) gloo_storage::LocalStorage::set(path, base64_data) .map_err(|e| DomainError::InvalidFileFormat(format!("Failed to store in localStorage: {}", e)))?; Ok(()) } // Mock implementation for testing on native targets #[cfg(not(target_arch = "wasm32"))] { // For testing purposes, we'll use a simple in-memory storage // In a real browser environment, this would use actual localStorage use base64::Engine; // Serialize to CBOR let data = serde_cbor::to_vec(file) .map_err(|e| DomainError::InvalidFileFormat(format!("Failed to serialize file: {}", e)))?; // Convert to base64 for storage let base64_data = base64::engine::general_purpose::STANDARD.encode(&data); // Store in mock storage let mock_storage = MOCK_STORAGE.get_or_init(|| Mutex::new(HashMap::new())); mock_storage.lock().unwrap().insert(path.to_string(), base64_data); Ok(()) } } fn load(&self, path: &str) -> Result { // Real implementation for WASM targets #[cfg(target_arch = "wasm32")] { use base64::Engine; use gloo_storage::Storage; // Load from browser localStorage (synchronous) let base64_data: String = gloo_storage::LocalStorage::get(path) .map_err(|e| DomainError::InvalidFileFormat(format!("Failed to load from localStorage: {}", e)))?; // Decode from base64 let data = base64::engine::general_purpose::STANDARD.decode(&base64_data) .map_err(|e| DomainError::InvalidFileFormat(format!("Failed to decode base64: {}", e)))?; // Deserialize from CBOR let file = serde_cbor::from_slice(&data) .map_err(|e| DomainError::InvalidFileFormat(format!("Failed to deserialize file: {}", e)))?; Ok(file) } // Mock implementation for testing on native targets #[cfg(not(target_arch = "wasm32"))] { use base64::Engine; // Load from mock storage let mock_storage = MOCK_STORAGE.get_or_init(|| Mutex::new(HashMap::new())); let mock_storage = mock_storage.lock().unwrap(); let base64_data = mock_storage.get(path) .ok_or_else(|| DomainError::InvalidFileFormat(format!("Key not found: {}", path)))?; // Decode from base64 let data = base64::engine::general_purpose::STANDARD.decode(base64_data) .map_err(|e| DomainError::InvalidFileFormat(format!("Failed to decode base64: {}", e)))?; // Deserialize from CBOR let file = serde_cbor::from_slice(&data) .map_err(|e| DomainError::InvalidFileFormat(format!("Failed to deserialize file: {}", e)))?; Ok(file) } } }