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}