Mountain/ApplicationState/Internal/Persistence/
MementoLoader.rs

1//! # MementoLoader Module (Internal)
2//!
3//! ## RESPONSIBILITIES
4//! Loads memento state from disk for application initialization and recovery.
5//! Synchronously loads memento from JSON file, handles missing files and
6//! corrupted data.
7//!
8//! ## ARCHITECTURAL ROLE
9//! MementoLoader is part of the **Internal::Persistence** module, handling
10//! memento loading operations.
11//!
12//! ## KEY COMPONENTS
13//! - LoadInitialMementoFromDisk: Main function for loading memento
14//!
15//! ## ERROR HANDLING
16//! - Returns empty HashMap if file doesn't exist
17//! - Creates backup and returns empty on parse error
18//! - Creates directory on read error
19//!
20//! ## LOGGING
21//! All operations are logged at appropriate levels (debug, info, warn, error).
22//!
23//! ## PERFORMANCE CONSIDERATIONS
24//! - Synchronous file I/O for initialization
25//! - Proper error handling and recovery
26//!
27//! ## TODO
28//! - [ ] Add checksum validation
29//! - [ ] Implement memento version migration
30//! - [ ] Add incremental loading support
31
32use std::{collections::HashMap, fs, path::Path};
33
34use CommonLibrary::Error::CommonError::CommonError;
35use serde_json::Value;
36use log::{debug, error, warn};
37
38/// Synchronously loads Memento storage data from a JSON file.
39/// Used during the initial `default()` setup of `ApplicationState`.
40///
41/// # Arguments
42/// * `StorageFilePath` - Path to the memento storage file
43///
44/// # Returns
45/// HashMap containing the memento data, or empty HashMap on error
46///
47/// # Behavior
48/// - Returns empty HashMap if file doesn't exist
49/// - Creates backup and returns empty on parse error
50/// - Creates directory on read error
51///
52/// # Errors
53/// Errors are logged but not propagated; default values are returned.
54pub fn LoadInitialMementoFromDisk(StorageFilePath:&Path) -> HashMap<String, Value> {
55	if !StorageFilePath.exists() {
56		debug!("[MementoLoader] Memento file does not exist: {}", StorageFilePath.display());
57		return HashMap::new();
58	}
59
60	match fs::read_to_string(StorageFilePath) {
61		Ok(Content) => {
62			serde_json::from_str(&Content).unwrap_or_else(|Error| {
63				error!(
64					"[MementoLoader] Failed to parse JSON from '{}': {}. Attempting recovery.",
65					StorageFilePath.display(),
66					Error
67				);
68
69				// Attempt recovery by creating backup and returning empty map
70				attempt_memento_recovery(StorageFilePath, &Content);
71				HashMap::new()
72			})
73		},
74
75		Err(Error) => {
76			error!(
77				"[MementoLoader] Failed to read '{}': {}. Attempting recovery.",
78				StorageFilePath.display(),
79				Error
80			);
81
82			// Attempt recovery by ensuring directory exists
83			if let Some(parent) = StorageFilePath.parent() {
84				if !parent.exists() {
85					if let Err(dir_error) = fs::create_dir_all(parent) {
86						warn!(
87							"[MementoLoader] Failed to create directory '{}': {}",
88							parent.display(),
89							dir_error
90						);
91					}
92				}
93			}
94
95			HashMap::new()
96		},
97	}
98}
99
100/// Robust memento loading with comprehensive error handling.
101///
102/// # Arguments
103/// * `StorageFilePath` - Path to the memento storage file
104///
105/// # Returns
106/// Result containing the memento HashMap or CommonError
107///
108/// # Errors
109/// Returns CommonError for file I/O or parse errors
110pub fn LoadMementoWithRecovery(StorageFilePath:&Path) -> Result<HashMap<String, Value>, CommonError> {
111	if !StorageFilePath.exists() {
112		debug!("[MementoLoader] Memento file does not exist: {}", StorageFilePath.display());
113		return Ok(HashMap::new());
114	}
115
116	let content = fs::read_to_string(StorageFilePath).map_err(|e| {
117		CommonError::FileSystemIO {
118			Path:StorageFilePath.to_path_buf(),
119			Description:format!("Failed to read memento file: {}", e),
120		}
121	})?;
122
123	serde_json::from_str(&content).map_err(|e| {
124		// Create backup of corrupted file
125		create_corrupted_backup(StorageFilePath, &content);
126		CommonError::SerializationError {
127			Description:format!("Failed to parse memento JSON from '{}': {}", StorageFilePath.display(), e),
128		}
129	})
130}
131
132/// Attempt recovery for corrupted memento files.
133///
134/// # Arguments
135/// * `file_path` - Path to the corrupted memento file
136/// * `corrupted_content` - The corrupted content to backup
137fn attempt_memento_recovery(file_path:&Path, corrupted_content:&str) {
138	let backup_path = file_path.with_extension("json.backup");
139
140	match fs::write(&backup_path, corrupted_content) {
141		Ok(()) => {
142			warn!(
143				"[MementoLoader] Created backup of corrupted memento at: {}",
144				backup_path.display()
145			);
146		},
147		Err(e) => {
148			error!(
149				"[MementoLoader] Failed to create backup of corrupted memento at '{}': {}",
150				backup_path.display(),
151				e
152			);
153		},
154	}
155}
156
157/// Create backup of corrupted file with timestamp.
158///
159/// # Arguments
160/// * `file_path` - Path to the corrupted file
161/// * `content` - The content to backup
162fn create_corrupted_backup(file_path:&Path, content:&str) {
163	let timestamp = chrono::Utc::now().format("%Y%m%d_%H%M%S");
164	let backup_path = file_path.with_extension(format!("json.corrupted.{}", timestamp));
165
166	if let Err(e) = fs::write(&backup_path, content) {
167		error!(
168			"[MementoLoader] Failed to create corrupted backup at '{}': {}",
169			backup_path.display(),
170			e
171		);
172	} else {
173		debug!("[MementoLoader] Created corrupted backup at: {}", backup_path.display());
174	}
175}