Skip to main content

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}