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