ryo_analysis/uuid_persistence.rs
1//! UUID persistence trait for symbol identity storage.
2//!
3//! This module defines the abstract interface for UUID storage.
4//! Implementations are provided by downstream crates (e.g., `ryo-storage`).
5//!
6//! # Design
7//!
8//! UUID mappings map `SymbolPath` (string) to `UUID` (string) for JSON compatibility.
9//! This allows symbol identity to persist across server restarts.
10//!
11//! # Example
12//!
13//! ```ignore
14//! use ryo_analysis::UuidPersistence;
15//!
16//! struct FileStorage { path: PathBuf }
17//!
18//! impl UuidPersistence for FileStorage {
19//! fn load(&self) -> Option<HashMap<String, String>> {
20//! let content = std::fs::read_to_string(&self.path).ok()?;
21//! serde_json::from_str(&content).ok()
22//! }
23//!
24//! fn save(&self, mappings: &HashMap<String, String>) -> Result<(), UuidPersistenceError> {
25//! let content = serde_json::to_string_pretty(mappings)?;
26//! std::fs::write(&self.path, content)?;
27//! Ok(())
28//! }
29//! }
30//! ```
31
32use std::collections::HashMap;
33
34/// Error type for UUID persistence operations.
35#[derive(Debug, thiserror::Error)]
36pub enum UuidPersistenceError {
37 /// I/O error during load/save.
38 #[error("I/O error: {0}")]
39 Io(#[from] std::io::Error),
40
41 /// Serialization/deserialization error.
42 #[error("Serialization error: {0}")]
43 Serialization(String),
44}
45
46impl From<serde_json::Error> for UuidPersistenceError {
47 fn from(e: serde_json::Error) -> Self {
48 UuidPersistenceError::Serialization(e.to_string())
49 }
50}
51
52/// Abstract trait for UUID persistence.
53///
54/// Implementations handle the actual storage mechanism (file, database, etc.).
55/// This trait is defined in `ryo-analysis` and implemented in `ryo-storage`.
56pub trait UuidPersistence: Send + Sync {
57 /// Load UUID mappings from storage.
58 ///
59 /// Returns `None` if no mappings exist (first run) or if loading fails.
60 /// Implementations should log errors internally if needed.
61 fn load(&self) -> Option<HashMap<String, String>>;
62
63 /// Save UUID mappings to storage.
64 ///
65 /// Creates the storage location if it doesn't exist.
66 fn save(&self, mappings: &HashMap<String, String>) -> Result<(), UuidPersistenceError>;
67}
68
69/// No-op implementation for testing or when persistence is disabled.
70#[derive(Debug, Clone, Default)]
71pub struct NoOpUuidPersistence;
72
73impl UuidPersistence for NoOpUuidPersistence {
74 fn load(&self) -> Option<HashMap<String, String>> {
75 None
76 }
77
78 fn save(&self, _mappings: &HashMap<String, String>) -> Result<(), UuidPersistenceError> {
79 Ok(())
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86
87 #[test]
88 fn test_noop_persistence() {
89 let storage = NoOpUuidPersistence;
90
91 // Load returns None
92 assert!(storage.load().is_none());
93
94 // Save succeeds
95 let mappings = HashMap::new();
96 assert!(storage.save(&mappings).is_ok());
97 }
98}