reputation_core/calculator/
builder.rs1use crate::{Calculator, Result, BuilderError, CalculatorConfig};
7
8#[derive(Debug, Clone)]
10pub struct BonusConfig {
11    pub mcp_bonus_per_level: f64,
13    pub identity_bonus: f64,
15    pub security_audit_bonus: f64,
17    pub open_source_bonus: f64,
19    pub age_bonus: f64,
21}
22
23impl Default for BonusConfig {
24    fn default() -> Self {
25        Self {
26            mcp_bonus_per_level: 5.0,
27            identity_bonus: 5.0,
28            security_audit_bonus: 7.0,
29            open_source_bonus: 3.0,
30            age_bonus: 5.0,
31        }
32    }
33}
34
35#[derive(Debug, Clone, Copy, PartialEq)]
37pub enum CalculatorPreset {
38    Testing,
40    Conservative,
42    Aggressive,
44    Default,
46}
47
48#[derive(Debug, Clone)]
72pub struct CalculatorBuilder {
73    confidence_k: Option<f64>,
74    prior_base: Option<f64>,
75    prior_max: Option<f64>,
76    bonuses: Option<BonusConfig>,
77    preset_applied: Option<CalculatorPreset>,
78}
79
80impl Default for CalculatorBuilder {
81    fn default() -> Self {
82        Self {
83            confidence_k: None,
84            prior_base: None,
85            prior_max: None,
86            bonuses: None,
87            preset_applied: None,
88        }
89    }
90}
91
92impl CalculatorBuilder {
93    pub fn new() -> Self {
95        Self::default()
96    }
97    
98    pub fn confidence_k(mut self, k: f64) -> Self {
114        self.confidence_k = Some(k);
115        self
116    }
117    
118    pub fn prior_base(mut self, base: f64) -> Self {
133        self.prior_base = Some(base);
134        self
135    }
136    
137    pub fn prior_max(mut self, max: f64) -> Self {
153        self.prior_max = Some(max);
154        self
155    }
156    
157    pub fn prior_bonuses(mut self, bonuses: BonusConfig) -> Self {
178        self.bonuses = Some(bonuses);
179        self
180    }
181    
182    pub fn preset(mut self, preset: CalculatorPreset) -> Self {
200        self.preset_applied = Some(preset);
201        
202        match preset {
204            CalculatorPreset::Testing => {
205                self.confidence_k = Some(5.0);
206                self.prior_base = Some(50.0);
207                self.prior_max = Some(80.0);
208            }
209            CalculatorPreset::Conservative => {
210                self.confidence_k = Some(30.0);
211                self.prior_base = Some(60.0);
212                self.prior_max = Some(80.0);
213            }
214            CalculatorPreset::Aggressive => {
215                self.confidence_k = Some(10.0);
216                self.prior_base = Some(40.0);
217                self.prior_max = Some(90.0);
218            }
219            CalculatorPreset::Default => {
220                self.confidence_k = Some(15.0);
221                self.prior_base = Some(50.0);
222                self.prior_max = Some(80.0);
223            }
224        }
225        
226        self
227    }
228    
229    pub fn from_config(mut self, config: CalculatorConfig) -> Self {
243        self.confidence_k = Some(config.confidence_k);
244        self.prior_base = Some(config.prior_base);
245        self.prior_max = Some(config.prior_max);
246        self
247    }
248    
249    pub fn config(&self) -> CalculatorConfig {
251        CalculatorConfig {
252            confidence_k: self.confidence_k.unwrap_or(15.0),
253            prior_base: self.prior_base.unwrap_or(50.0),
254            prior_max: self.prior_max.unwrap_or(80.0),
255        }
256    }
257    
258    pub fn build(self) -> Result<Calculator> {
279        let config = self.config();
280        
281        if config.confidence_k <= 0.0 {
283            return Err(BuilderError::InvalidConfig(
284                "confidence_k must be positive".to_string()
285            ).into());
286        }
287        
288        if config.prior_base < 0.0 || config.prior_base > 100.0 {
289            return Err(BuilderError::InvalidConfig(
290                "prior_base must be between 0 and 100".to_string()
291            ).into());
292        }
293        
294        if config.prior_max <= config.prior_base {
295            return Err(BuilderError::InvalidConfig(
296                format!("prior_max must be greater than prior_base ({})", config.prior_base)
297            ).into());
298        }
299        
300        if config.prior_max > 100.0 {
301            return Err(BuilderError::InvalidConfig(
302                "prior_max must not exceed 100".to_string()
303            ).into());
304        }
305        
306        Calculator::new(config.confidence_k, config.prior_base, config.prior_max)
310    }
311    
312    pub fn reset(mut self) -> Self {
314        self.confidence_k = None;
315        self.prior_base = None;
316        self.prior_max = None;
317        self.bonuses = None;
318        self.preset_applied = None;
319        self
320    }
321}
322
323#[cfg(test)]
324mod tests {
325    use super::*;
326    
327    #[test]
328    fn test_builder_defaults() {
329        let calc = CalculatorBuilder::new().build().unwrap();
330        assert_eq!(calc.confidence_k, 15.0);
331        assert_eq!(calc.prior_base, 50.0);
332        assert_eq!(calc.prior_max, 80.0);
333    }
334    
335    #[test]
336    fn test_builder_custom_values() {
337        let calc = CalculatorBuilder::new()
338            .confidence_k(25.0)
339            .prior_base(55.0)
340            .prior_max(85.0)
341            .build()
342            .unwrap();
343            
344        assert_eq!(calc.confidence_k, 25.0);
345        assert_eq!(calc.prior_base, 55.0);
346        assert_eq!(calc.prior_max, 85.0);
347    }
348    
349    #[test]
350    fn test_preset_testing() {
351        let calc = CalculatorBuilder::new()
352            .preset(CalculatorPreset::Testing)
353            .build()
354            .unwrap();
355            
356        assert_eq!(calc.confidence_k, 5.0);
357        assert_eq!(calc.prior_base, 50.0);
358        assert_eq!(calc.prior_max, 80.0);
359    }
360    
361    #[test]
362    fn test_preset_conservative() {
363        let calc = CalculatorBuilder::new()
364            .preset(CalculatorPreset::Conservative)
365            .build()
366            .unwrap();
367            
368        assert_eq!(calc.confidence_k, 30.0);
369        assert_eq!(calc.prior_base, 60.0);
370        assert_eq!(calc.prior_max, 80.0);
371    }
372    
373    #[test]
374    fn test_preset_aggressive() {
375        let calc = CalculatorBuilder::new()
376            .preset(CalculatorPreset::Aggressive)
377            .build()
378            .unwrap();
379            
380        assert_eq!(calc.confidence_k, 10.0);
381        assert_eq!(calc.prior_base, 40.0);
382        assert_eq!(calc.prior_max, 90.0);
383    }
384    
385    #[test]
386    fn test_preset_override() {
387        let calc = CalculatorBuilder::new()
389            .preset(CalculatorPreset::Testing)
390            .confidence_k(8.0)  .build()
392            .unwrap();
393            
394        assert_eq!(calc.confidence_k, 8.0);  assert_eq!(calc.prior_base, 50.0);   assert_eq!(calc.prior_max, 80.0);    }
398    
399    #[test]
400    fn test_validation_confidence_k() {
401        let result = CalculatorBuilder::new()
402            .confidence_k(0.0)
403            .build();
404        assert!(result.is_err());
405        
406        let result = CalculatorBuilder::new()
407            .confidence_k(-5.0)
408            .build();
409        assert!(result.is_err());
410    }
411    
412    #[test]
413    fn test_validation_prior_base() {
414        let result = CalculatorBuilder::new()
415            .prior_base(-10.0)
416            .build();
417        assert!(result.is_err());
418        
419        let result = CalculatorBuilder::new()
420            .prior_base(105.0)
421            .build();
422        assert!(result.is_err());
423    }
424    
425    #[test]
426    fn test_validation_prior_max() {
427        let result = CalculatorBuilder::new()
429            .prior_base(60.0)
430            .prior_max(50.0)
431            .build();
432        assert!(result.is_err());
433        
434        let result = CalculatorBuilder::new()
436            .prior_base(60.0)
437            .prior_max(60.0)
438            .build();
439        assert!(result.is_err());
440        
441        let result = CalculatorBuilder::new()
443            .prior_max(105.0)
444            .build();
445        assert!(result.is_err());
446    }
447    
448    #[test]
449    fn test_builder_clone() {
450        let builder = CalculatorBuilder::new()
451            .confidence_k(20.0)
452            .prior_base(60.0);
453            
454        let builder2 = builder.clone()
455            .prior_max(85.0);
456            
457        let calc1 = builder.build().unwrap();
458        let calc2 = builder2.build().unwrap();
459        
460        assert_eq!(calc1.confidence_k, 20.0);
461        assert_eq!(calc1.prior_max, 80.0);  assert_eq!(calc2.confidence_k, 20.0);
464        assert_eq!(calc2.prior_max, 85.0);  }
466    
467    #[test]
468    fn test_builder_reset() {
469        let builder = CalculatorBuilder::new()
470            .confidence_k(20.0)
471            .prior_base(60.0)
472            .reset();
473            
474        let calc = builder.build().unwrap();
475        
476        assert_eq!(calc.confidence_k, 15.0);
478        assert_eq!(calc.prior_base, 50.0);
479        assert_eq!(calc.prior_max, 80.0);
480    }
481    
482    #[test]
483    fn test_from_config() {
484        let config = CalculatorConfig {
485            confidence_k: 22.0,
486            prior_base: 58.0,
487            prior_max: 88.0,
488        };
489        
490        let calc = CalculatorBuilder::new()
491            .from_config(config)
492            .build()
493            .unwrap();
494            
495        assert_eq!(calc.confidence_k, 22.0);
496        assert_eq!(calc.prior_base, 58.0);
497        assert_eq!(calc.prior_max, 88.0);
498    }
499    
500    #[test]
501    fn test_bonus_config_default() {
502        let bonuses = BonusConfig::default();
503        assert_eq!(bonuses.mcp_bonus_per_level, 5.0);
504        assert_eq!(bonuses.identity_bonus, 5.0);
505        assert_eq!(bonuses.security_audit_bonus, 7.0);
506        assert_eq!(bonuses.open_source_bonus, 3.0);
507        assert_eq!(bonuses.age_bonus, 5.0);
508    }
509    
510    #[test]
511    fn test_method_chaining() {
512        let _calc = CalculatorBuilder::new()
514            .preset(CalculatorPreset::Testing)
515            .confidence_k(10.0)
516            .prior_base(55.0)
517            .prior_max(85.0)
518            .prior_bonuses(BonusConfig::default())
519            .build()
520            .unwrap();
521    }
522}