ryo-analysis 0.1.0

Code graph and discovery engine for the RYO project
Documentation
//! UUID persistence trait for symbol identity storage.
//!
//! This module defines the abstract interface for UUID storage.
//! Implementations are provided by downstream crates (e.g., `ryo-storage`).
//!
//! # Design
//!
//! UUID mappings map `SymbolPath` (string) to `UUID` (string) for JSON compatibility.
//! This allows symbol identity to persist across server restarts.
//!
//! # Example
//!
//! ```ignore
//! use ryo_analysis::UuidPersistence;
//!
//! struct FileStorage { path: PathBuf }
//!
//! impl UuidPersistence for FileStorage {
//!     fn load(&self) -> Option<HashMap<String, String>> {
//!         let content = std::fs::read_to_string(&self.path).ok()?;
//!         serde_json::from_str(&content).ok()
//!     }
//!
//!     fn save(&self, mappings: &HashMap<String, String>) -> Result<(), UuidPersistenceError> {
//!         let content = serde_json::to_string_pretty(mappings)?;
//!         std::fs::write(&self.path, content)?;
//!         Ok(())
//!     }
//! }
//! ```

use std::collections::HashMap;

/// Error type for UUID persistence operations.
#[derive(Debug, thiserror::Error)]
pub enum UuidPersistenceError {
    /// I/O error during load/save.
    #[error("I/O error: {0}")]
    Io(#[from] std::io::Error),

    /// Serialization/deserialization error.
    #[error("Serialization error: {0}")]
    Serialization(String),
}

impl From<serde_json::Error> for UuidPersistenceError {
    fn from(e: serde_json::Error) -> Self {
        UuidPersistenceError::Serialization(e.to_string())
    }
}

/// Abstract trait for UUID persistence.
///
/// Implementations handle the actual storage mechanism (file, database, etc.).
/// This trait is defined in `ryo-analysis` and implemented in `ryo-storage`.
pub trait UuidPersistence: Send + Sync {
    /// Load UUID mappings from storage.
    ///
    /// Returns `None` if no mappings exist (first run) or if loading fails.
    /// Implementations should log errors internally if needed.
    fn load(&self) -> Option<HashMap<String, String>>;

    /// Save UUID mappings to storage.
    ///
    /// Creates the storage location if it doesn't exist.
    fn save(&self, mappings: &HashMap<String, String>) -> Result<(), UuidPersistenceError>;
}

/// No-op implementation for testing or when persistence is disabled.
#[derive(Debug, Clone, Default)]
pub struct NoOpUuidPersistence;

impl UuidPersistence for NoOpUuidPersistence {
    fn load(&self) -> Option<HashMap<String, String>> {
        None
    }

    fn save(&self, _mappings: &HashMap<String, String>) -> Result<(), UuidPersistenceError> {
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_noop_persistence() {
        let storage = NoOpUuidPersistence;

        // Load returns None
        assert!(storage.load().is_none());

        // Save succeeds
        let mappings = HashMap::new();
        assert!(storage.save(&mappings).is_ok());
    }
}