argumentation_values/
types.rs1use smallvec::SmallVec;
4use std::collections::HashMap;
5use std::hash::Hash;
6
7#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
13pub struct Value(String);
14
15impl Value {
16 pub fn new(s: impl Into<String>) -> Self {
18 Self(s.into())
19 }
20
21 pub fn as_str(&self) -> &str {
23 &self.0
24 }
25}
26
27impl std::fmt::Display for Value {
28 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29 write!(f, "{}", self.0)
30 }
31}
32
33impl From<&str> for Value {
34 fn from(s: &str) -> Self {
35 Self(s.to_string())
36 }
37}
38
39impl From<String> for Value {
40 fn from(s: String) -> Self {
41 Self(s)
42 }
43}
44
45#[derive(Debug, Clone)]
55pub struct ValueAssignment<A: Eq + Hash> {
56 promoted: HashMap<A, SmallVec<[Value; 1]>>,
58}
59
60impl<A: Eq + Hash> Default for ValueAssignment<A> {
61 fn default() -> Self {
62 Self {
63 promoted: HashMap::new(),
64 }
65 }
66}
67
68impl<A: Eq + Hash + Clone> ValueAssignment<A> {
69 pub fn new() -> Self {
71 Self::default()
72 }
73
74 pub fn promote(&mut self, arg: A, value: Value) -> &mut Self {
77 let entry = self.promoted.entry(arg).or_default();
78 if !entry.contains(&value) {
79 entry.push(value);
80 }
81 self
82 }
83
84 pub fn values(&self, arg: &A) -> &[Value] {
87 self.promoted
88 .get(arg)
89 .map(|v| v.as_slice())
90 .unwrap_or(&[])
91 }
92
93 pub fn entries(&self) -> impl Iterator<Item = (&A, &[Value])> {
95 self.promoted.iter().map(|(k, v)| (k, v.as_slice()))
96 }
97
98 pub fn distinct_values(&self) -> std::collections::BTreeSet<&Value> {
100 self.promoted.values().flatten().collect()
101 }
102}
103
104#[derive(Debug, Clone, Default)]
127pub struct Audience {
128 tiers: Vec<Vec<Value>>,
130}
131
132impl Audience {
133 pub fn new() -> Self {
135 Self::default()
136 }
137
138 pub fn total<I: IntoIterator<Item = Value>>(ranked: I) -> Self {
141 Self {
142 tiers: ranked.into_iter().map(|v| vec![v]).collect(),
143 }
144 }
145
146 pub fn from_tiers(tiers: Vec<Vec<Value>>) -> Self {
149 Self { tiers }
150 }
151
152 pub fn prefers(&self, a: &Value, b: &Value) -> bool {
155 match (self.rank(a), self.rank(b)) {
156 (Some(ra), Some(rb)) => ra < rb,
157 _ => false,
158 }
159 }
160
161 pub fn rank(&self, v: &Value) -> Option<usize> {
167 self.tiers
168 .iter()
169 .position(|tier| tier.iter().any(|x| x == v))
170 }
171
172 pub fn values(&self) -> impl Iterator<Item = &Value> {
174 self.tiers.iter().flatten()
175 }
176
177 pub fn value_count(&self) -> usize {
179 self.tiers.iter().map(|t| t.len()).sum()
180 }
181
182 pub fn tier_count(&self) -> usize {
184 self.tiers.len()
185 }
186}
187
188#[cfg(test)]
189mod tests {
190 use super::*;
191
192 #[test]
193 fn value_assignment_dedupes_promotions() {
194 let mut va: ValueAssignment<&str> = ValueAssignment::new();
195 va.promote("a", Value::new("life"));
196 va.promote("a", Value::new("life"));
197 assert_eq!(va.values(&"a").len(), 1);
198 }
199
200 #[test]
201 fn value_assignment_accepts_multi_value() {
202 let mut va: ValueAssignment<&str> = ValueAssignment::new();
203 va.promote("a", Value::new("life"));
204 va.promote("a", Value::new("autonomy"));
205 assert_eq!(va.values(&"a").len(), 2);
206 }
207
208 #[test]
209 fn audience_total_orders_strictly() {
210 let a = Audience::total([Value::new("life"), Value::new("property")]);
211 assert!(a.prefers(&Value::new("life"), &Value::new("property")));
212 assert!(!a.prefers(&Value::new("property"), &Value::new("life")));
213 }
214
215 #[test]
216 fn audience_unranked_values_are_incomparable() {
217 let a = Audience::total([Value::new("life")]);
218 assert!(!a.prefers(&Value::new("property"), &Value::new("life")));
219 assert!(!a.prefers(&Value::new("life"), &Value::new("property")));
220 }
221
222 #[test]
223 fn audience_intra_tier_values_are_incomparable() {
224 let a = Audience::from_tiers(vec![vec![Value::new("life"), Value::new("liberty")]]);
225 assert!(!a.prefers(&Value::new("life"), &Value::new("liberty")));
226 assert!(!a.prefers(&Value::new("liberty"), &Value::new("life")));
227 }
228
229 #[test]
230 fn audience_distinct_values_count() {
231 let a = Audience::from_tiers(vec![
232 vec![Value::new("a"), Value::new("b")],
233 vec![Value::new("c")],
234 ]);
235 assert_eq!(a.value_count(), 3);
236 assert_eq!(a.tier_count(), 2);
237 }
238
239 #[test]
240 fn audience_rank_returns_tier_index() {
241 let a = Audience::from_tiers(vec![
242 vec![Value::new("life"), Value::new("liberty")],
243 vec![Value::new("property")],
244 ]);
245 assert_eq!(a.rank(&Value::new("life")), Some(0));
246 assert_eq!(a.rank(&Value::new("liberty")), Some(0));
247 assert_eq!(a.rank(&Value::new("property")), Some(1));
248 assert_eq!(a.rank(&Value::new("comfort")), None);
249 }
250}