khive_fold/objective/
selection.rs1#[cfg(feature = "serde")]
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone)]
8#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
9#[must_use = "selections should be used after creation"]
10pub struct Selection<T> {
11 pub item: T,
13 pub score: f64,
15 #[cfg_attr(feature = "serde", serde(default = "default_precision"))]
20 pub precision: f64,
21 pub index: usize,
23 pub considered: usize,
25 pub passed: usize,
27 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
29 pub reason: Option<String>,
30}
31
32fn default_precision() -> f64 {
33 1.0
34}
35
36impl<T> Selection<T> {
37 pub fn new(item: T, score: f64, index: usize) -> Self {
39 Self {
40 item,
41 score,
42 precision: 1.0,
43 index,
44 considered: 1,
45 passed: 1,
46 reason: None,
47 }
48 }
49
50 pub fn with_precision(mut self, precision: f64) -> Self {
54 self.precision = precision;
55 self
56 }
57
58 pub fn with_considered(mut self, n: usize) -> Self {
60 self.considered = n;
61 self
62 }
63
64 pub fn with_passed(mut self, n: usize) -> Self {
66 self.passed = n;
67 self
68 }
69
70 pub fn with_reason(mut self, reason: impl Into<String>) -> Self {
72 self.reason = Some(reason.into());
73 self
74 }
75
76 pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Selection<U> {
78 Selection {
79 item: f(self.item),
80 score: self.score,
81 precision: self.precision,
82 index: self.index,
83 considered: self.considered,
84 passed: self.passed,
85 reason: self.reason,
86 }
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 #[test]
95 fn precision_default_is_one() {
96 let sel = Selection::new(42i32, 0.8, 0);
97 assert_eq!(sel.precision, 1.0);
98 }
99
100 #[test]
101 fn with_precision_sets_field() {
102 let sel = Selection::new(42i32, 0.8, 0).with_precision(0.5);
103 assert_eq!(sel.precision, 0.5);
104 }
105
106 #[test]
107 fn map_propagates_precision() {
108 let sel = Selection::new(42i32, 0.8, 0).with_precision(0.75);
109 let mapped = sel.map(|v| v.to_string());
110 assert_eq!(mapped.precision, 0.75);
111 assert_eq!(mapped.item, "42");
112 assert_eq!(mapped.score, 0.8);
113 }
114
115 #[test]
116 fn map_preserves_all_stats() {
117 let sel = Selection::new(1i32, 0.5, 2)
118 .with_precision(0.6)
119 .with_considered(10)
120 .with_passed(7)
121 .with_reason("test");
122 let mapped = sel.map(|v| v * 2);
123 assert_eq!(mapped.item, 2);
124 assert_eq!(mapped.score, 0.5);
125 assert_eq!(mapped.precision, 0.6);
126 assert_eq!(mapped.index, 2);
127 assert_eq!(mapped.considered, 10);
128 assert_eq!(mapped.passed, 7);
129 assert_eq!(mapped.reason.as_deref(), Some("test"));
130 }
131}