reputation-core 0.1.0

Core calculation engine for the KnowThat Reputation System with advanced scoring algorithms
Documentation
use serde::{Deserialize, Serialize};
use crate::{Result, BuilderError};
use std::path::Path;

/// Configuration for the reputation calculator
/// 
/// Controls how reputation scores are calculated, including
/// confidence growth rate and prior score parameters.
/// 
/// # Example
/// 
/// ```
/// use reputation_core::CalculatorConfig;
/// 
/// let config = CalculatorConfig {
///     confidence_k: 20.0,  // Slower confidence growth
///     prior_base: 60.0,    // Higher starting score
///     prior_max: 90.0,     // Higher maximum prior
/// };
/// ```
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CalculatorConfig {
    /// Confidence growth parameter (k in the formula)
    /// Higher values = slower confidence growth
    /// Default: 15.0
    #[serde(default = "default_confidence_k")]
    pub confidence_k: f64,
    
    /// Base prior score for all agents
    /// Default: 50.0
    #[serde(default = "default_prior_base")]
    pub prior_base: f64,
    
    /// Maximum possible prior score
    /// Default: 80.0
    #[serde(default = "default_prior_max")]
    pub prior_max: f64,
}

fn default_confidence_k() -> f64 { 15.0 }
fn default_prior_base() -> f64 { 50.0 }
fn default_prior_max() -> f64 { 80.0 }

impl Default for CalculatorConfig {
    fn default() -> Self {
        Self {
            confidence_k: default_confidence_k(),
            prior_base: default_prior_base(),
            prior_max: default_prior_max(),
        }
    }
}

impl CalculatorConfig {
    /// Load configuration from a JSON file
    /// 
    /// # Example
    /// 
    /// ```no_run
    /// use reputation_core::CalculatorConfig;
    /// use std::path::Path;
    /// 
    /// let config = CalculatorConfig::from_file(Path::new("config/calculator.json"))
    ///     .expect("Failed to load config");
    /// ```
    pub fn from_file(path: &Path) -> Result<Self> {
        let content = std::fs::read_to_string(path)
            .map_err(|e| BuilderError::InvalidConfig(format!("Failed to read config file: {}", e)))?;
        
        serde_json::from_str(&content)
            .map_err(|e| BuilderError::InvalidConfig(format!("Failed to parse config JSON: {}", e)).into())
    }
    
    /// Save configuration to a JSON file
    /// 
    /// # Example
    /// 
    /// ```no_run
    /// use reputation_core::CalculatorConfig;
    /// use std::path::Path;
    /// 
    /// let config = CalculatorConfig::default();
    /// config.to_file(Path::new("config/calculator.json"))
    ///     .expect("Failed to save config");
    /// ```
    pub fn to_file(&self, path: &Path) -> Result<()> {
        let content = serde_json::to_string_pretty(self)
            .map_err(|e| BuilderError::InvalidConfig(format!("Failed to serialize config: {}", e)))?;
        
        std::fs::write(path, content)
            .map_err(|e| BuilderError::InvalidConfig(format!("Failed to write config file: {}", e)).into())
    }
}

/// Configuration presets for common scenarios
pub mod presets {
    use super::CalculatorConfig;
    
    /// Fast confidence growth for testing environments
    pub fn testing() -> CalculatorConfig {
        CalculatorConfig {
            confidence_k: 5.0,
            prior_base: 50.0,
            prior_max: 80.0,
        }
    }
    
    /// Conservative configuration with slow confidence growth
    pub fn conservative() -> CalculatorConfig {
        CalculatorConfig {
            confidence_k: 30.0,
            prior_base: 60.0,
            prior_max: 80.0,
        }
    }
    
    /// Aggressive configuration with fast confidence growth
    pub fn aggressive() -> CalculatorConfig {
        CalculatorConfig {
            confidence_k: 10.0,
            prior_base: 40.0,
            prior_max: 90.0,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_default_config() {
        let config = CalculatorConfig::default();
        assert_eq!(config.confidence_k, 15.0);
        assert_eq!(config.prior_base, 50.0);
        assert_eq!(config.prior_max, 80.0);
    }
    
    #[test]
    fn test_presets() {
        let testing = presets::testing();
        assert_eq!(testing.confidence_k, 5.0);
        
        let conservative = presets::conservative();
        assert_eq!(conservative.confidence_k, 30.0);
        
        let aggressive = presets::aggressive();
        assert_eq!(aggressive.confidence_k, 10.0);
    }
    
    #[test]
    fn test_config_serialization() {
        let config = CalculatorConfig {
            confidence_k: 25.0,
            prior_base: 55.0,
            prior_max: 85.0,
        };
        
        let json = serde_json::to_string(&config).unwrap();
        let deserialized: CalculatorConfig = serde_json::from_str(&json).unwrap();
        
        assert_eq!(config.confidence_k, deserialized.confidence_k);
        assert_eq!(config.prior_base, deserialized.prior_base);
        assert_eq!(config.prior_max, deserialized.prior_max);
    }
    
    #[test]
    fn test_partial_config_deserialization() {
        // Only specify one field, others should use defaults
        let json = r#"{"confidence_k": 25.0}"#;
        let config: CalculatorConfig = serde_json::from_str(json).unwrap();
        
        assert_eq!(config.confidence_k, 25.0);
        assert_eq!(config.prior_base, 50.0);  // default
        assert_eq!(config.prior_max, 80.0);   // default
    }
}