use crate::{Calculator, Result, BuilderError, CalculatorConfig};
#[derive(Debug, Clone)]
pub struct BonusConfig {
pub mcp_bonus_per_level: f64,
pub identity_bonus: f64,
pub security_audit_bonus: f64,
pub open_source_bonus: f64,
pub age_bonus: f64,
}
impl Default for BonusConfig {
fn default() -> Self {
Self {
mcp_bonus_per_level: 5.0,
identity_bonus: 5.0,
security_audit_bonus: 7.0,
open_source_bonus: 3.0,
age_bonus: 5.0,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum CalculatorPreset {
Testing,
Conservative,
Aggressive,
Default,
}
#[derive(Debug, Clone)]
pub struct CalculatorBuilder {
confidence_k: Option<f64>,
prior_base: Option<f64>,
prior_max: Option<f64>,
bonuses: Option<BonusConfig>,
preset_applied: Option<CalculatorPreset>,
}
impl Default for CalculatorBuilder {
fn default() -> Self {
Self {
confidence_k: None,
prior_base: None,
prior_max: None,
bonuses: None,
preset_applied: None,
}
}
}
impl CalculatorBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn confidence_k(mut self, k: f64) -> Self {
self.confidence_k = Some(k);
self
}
pub fn prior_base(mut self, base: f64) -> Self {
self.prior_base = Some(base);
self
}
pub fn prior_max(mut self, max: f64) -> Self {
self.prior_max = Some(max);
self
}
pub fn prior_bonuses(mut self, bonuses: BonusConfig) -> Self {
self.bonuses = Some(bonuses);
self
}
pub fn preset(mut self, preset: CalculatorPreset) -> Self {
self.preset_applied = Some(preset);
match preset {
CalculatorPreset::Testing => {
self.confidence_k = Some(5.0);
self.prior_base = Some(50.0);
self.prior_max = Some(80.0);
}
CalculatorPreset::Conservative => {
self.confidence_k = Some(30.0);
self.prior_base = Some(60.0);
self.prior_max = Some(80.0);
}
CalculatorPreset::Aggressive => {
self.confidence_k = Some(10.0);
self.prior_base = Some(40.0);
self.prior_max = Some(90.0);
}
CalculatorPreset::Default => {
self.confidence_k = Some(15.0);
self.prior_base = Some(50.0);
self.prior_max = Some(80.0);
}
}
self
}
pub fn from_config(mut self, config: CalculatorConfig) -> Self {
self.confidence_k = Some(config.confidence_k);
self.prior_base = Some(config.prior_base);
self.prior_max = Some(config.prior_max);
self
}
pub fn config(&self) -> CalculatorConfig {
CalculatorConfig {
confidence_k: self.confidence_k.unwrap_or(15.0),
prior_base: self.prior_base.unwrap_or(50.0),
prior_max: self.prior_max.unwrap_or(80.0),
}
}
pub fn build(self) -> Result<Calculator> {
let config = self.config();
if config.confidence_k <= 0.0 {
return Err(BuilderError::InvalidConfig(
"confidence_k must be positive".to_string()
).into());
}
if config.prior_base < 0.0 || config.prior_base > 100.0 {
return Err(BuilderError::InvalidConfig(
"prior_base must be between 0 and 100".to_string()
).into());
}
if config.prior_max <= config.prior_base {
return Err(BuilderError::InvalidConfig(
format!("prior_max must be greater than prior_base ({})", config.prior_base)
).into());
}
if config.prior_max > 100.0 {
return Err(BuilderError::InvalidConfig(
"prior_max must not exceed 100".to_string()
).into());
}
Calculator::new(config.confidence_k, config.prior_base, config.prior_max)
}
pub fn reset(mut self) -> Self {
self.confidence_k = None;
self.prior_base = None;
self.prior_max = None;
self.bonuses = None;
self.preset_applied = None;
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_builder_defaults() {
let calc = CalculatorBuilder::new().build().unwrap();
assert_eq!(calc.confidence_k, 15.0);
assert_eq!(calc.prior_base, 50.0);
assert_eq!(calc.prior_max, 80.0);
}
#[test]
fn test_builder_custom_values() {
let calc = CalculatorBuilder::new()
.confidence_k(25.0)
.prior_base(55.0)
.prior_max(85.0)
.build()
.unwrap();
assert_eq!(calc.confidence_k, 25.0);
assert_eq!(calc.prior_base, 55.0);
assert_eq!(calc.prior_max, 85.0);
}
#[test]
fn test_preset_testing() {
let calc = CalculatorBuilder::new()
.preset(CalculatorPreset::Testing)
.build()
.unwrap();
assert_eq!(calc.confidence_k, 5.0);
assert_eq!(calc.prior_base, 50.0);
assert_eq!(calc.prior_max, 80.0);
}
#[test]
fn test_preset_conservative() {
let calc = CalculatorBuilder::new()
.preset(CalculatorPreset::Conservative)
.build()
.unwrap();
assert_eq!(calc.confidence_k, 30.0);
assert_eq!(calc.prior_base, 60.0);
assert_eq!(calc.prior_max, 80.0);
}
#[test]
fn test_preset_aggressive() {
let calc = CalculatorBuilder::new()
.preset(CalculatorPreset::Aggressive)
.build()
.unwrap();
assert_eq!(calc.confidence_k, 10.0);
assert_eq!(calc.prior_base, 40.0);
assert_eq!(calc.prior_max, 90.0);
}
#[test]
fn test_preset_override() {
let calc = CalculatorBuilder::new()
.preset(CalculatorPreset::Testing)
.confidence_k(8.0) .build()
.unwrap();
assert_eq!(calc.confidence_k, 8.0); assert_eq!(calc.prior_base, 50.0); assert_eq!(calc.prior_max, 80.0); }
#[test]
fn test_validation_confidence_k() {
let result = CalculatorBuilder::new()
.confidence_k(0.0)
.build();
assert!(result.is_err());
let result = CalculatorBuilder::new()
.confidence_k(-5.0)
.build();
assert!(result.is_err());
}
#[test]
fn test_validation_prior_base() {
let result = CalculatorBuilder::new()
.prior_base(-10.0)
.build();
assert!(result.is_err());
let result = CalculatorBuilder::new()
.prior_base(105.0)
.build();
assert!(result.is_err());
}
#[test]
fn test_validation_prior_max() {
let result = CalculatorBuilder::new()
.prior_base(60.0)
.prior_max(50.0)
.build();
assert!(result.is_err());
let result = CalculatorBuilder::new()
.prior_base(60.0)
.prior_max(60.0)
.build();
assert!(result.is_err());
let result = CalculatorBuilder::new()
.prior_max(105.0)
.build();
assert!(result.is_err());
}
#[test]
fn test_builder_clone() {
let builder = CalculatorBuilder::new()
.confidence_k(20.0)
.prior_base(60.0);
let builder2 = builder.clone()
.prior_max(85.0);
let calc1 = builder.build().unwrap();
let calc2 = builder2.build().unwrap();
assert_eq!(calc1.confidence_k, 20.0);
assert_eq!(calc1.prior_max, 80.0);
assert_eq!(calc2.confidence_k, 20.0);
assert_eq!(calc2.prior_max, 85.0); }
#[test]
fn test_builder_reset() {
let builder = CalculatorBuilder::new()
.confidence_k(20.0)
.prior_base(60.0)
.reset();
let calc = builder.build().unwrap();
assert_eq!(calc.confidence_k, 15.0);
assert_eq!(calc.prior_base, 50.0);
assert_eq!(calc.prior_max, 80.0);
}
#[test]
fn test_from_config() {
let config = CalculatorConfig {
confidence_k: 22.0,
prior_base: 58.0,
prior_max: 88.0,
};
let calc = CalculatorBuilder::new()
.from_config(config)
.build()
.unwrap();
assert_eq!(calc.confidence_k, 22.0);
assert_eq!(calc.prior_base, 58.0);
assert_eq!(calc.prior_max, 88.0);
}
#[test]
fn test_bonus_config_default() {
let bonuses = BonusConfig::default();
assert_eq!(bonuses.mcp_bonus_per_level, 5.0);
assert_eq!(bonuses.identity_bonus, 5.0);
assert_eq!(bonuses.security_audit_bonus, 7.0);
assert_eq!(bonuses.open_source_bonus, 3.0);
assert_eq!(bonuses.age_bonus, 5.0);
}
#[test]
fn test_method_chaining() {
let _calc = CalculatorBuilder::new()
.preset(CalculatorPreset::Testing)
.confidence_k(10.0)
.prior_base(55.0)
.prior_max(85.0)
.prior_bonuses(BonusConfig::default())
.build()
.unwrap();
}
}