argumentation_weighted_bipolar/
framework.rs1use crate::error::Error;
4use crate::types::{AttackWeight, WeightedAttack, WeightedSupport};
5use std::collections::HashSet;
6use std::hash::Hash;
7
8#[derive(Debug, Clone)]
13pub struct WeightedBipolarFramework<A: Clone + Eq + Hash> {
14 arguments: HashSet<A>,
15 attacks: Vec<WeightedAttack<A>>,
16 supports: Vec<WeightedSupport<A>>,
17}
18
19impl<A: Clone + Eq + Hash> Default for WeightedBipolarFramework<A> {
20 fn default() -> Self {
21 Self::new()
22 }
23}
24
25impl<A: Clone + Eq + Hash> WeightedBipolarFramework<A> {
26 #[must_use]
28 pub fn new() -> Self {
29 Self {
30 arguments: HashSet::new(),
31 attacks: Vec::new(),
32 supports: Vec::new(),
33 }
34 }
35
36 pub fn add_argument(&mut self, a: A) {
38 self.arguments.insert(a);
39 }
40
41 pub fn add_weighted_attack(
43 &mut self,
44 attacker: A,
45 target: A,
46 weight: f64,
47 ) -> Result<(), Error> {
48 let w = AttackWeight::new(weight).map_err(|_| Error::InvalidWeight { weight })?;
49 self.arguments.insert(attacker.clone());
50 self.arguments.insert(target.clone());
51 self.attacks.push(WeightedAttack {
52 attacker,
53 target,
54 weight: w,
55 });
56 Ok(())
57 }
58
59 pub fn add_weighted_support(
62 &mut self,
63 supporter: A,
64 supported: A,
65 weight: f64,
66 ) -> Result<(), Error> {
67 let support = WeightedSupport::new(supporter.clone(), supported.clone(), weight)?;
68 self.arguments.insert(supporter);
69 self.arguments.insert(supported);
70 self.supports.push(support);
71 Ok(())
72 }
73
74 pub fn arguments(&self) -> impl Iterator<Item = &A> {
76 self.arguments.iter()
77 }
78
79 pub fn attacks(&self) -> impl Iterator<Item = &WeightedAttack<A>> {
81 self.attacks.iter()
82 }
83
84 pub fn supports(&self) -> impl Iterator<Item = &WeightedSupport<A>> {
86 self.supports.iter()
87 }
88
89 #[must_use]
91 pub fn edge_count(&self) -> usize {
92 self.attacks.len() + self.supports.len()
93 }
94
95 #[must_use]
97 pub fn argument_count(&self) -> usize {
98 self.arguments.len()
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105
106 #[test]
107 fn new_framework_is_empty() {
108 let wbf: WeightedBipolarFramework<&str> = WeightedBipolarFramework::new();
109 assert_eq!(wbf.argument_count(), 0);
110 assert_eq!(wbf.edge_count(), 0);
111 }
112
113 #[test]
114 fn adding_weighted_attack_adds_endpoints() {
115 let mut wbf = WeightedBipolarFramework::new();
116 wbf.add_weighted_attack("a", "b", 0.5).unwrap();
117 assert_eq!(wbf.argument_count(), 2);
118 assert_eq!(wbf.edge_count(), 1);
119 }
120
121 #[test]
122 fn adding_weighted_support_adds_endpoints() {
123 let mut wbf = WeightedBipolarFramework::new();
124 wbf.add_weighted_support("a", "b", 0.5).unwrap();
125 assert_eq!(wbf.argument_count(), 2);
126 assert_eq!(wbf.edge_count(), 1);
127 }
128
129 #[test]
130 fn invalid_attack_weight_rejected() {
131 let mut wbf = WeightedBipolarFramework::new();
132 let err = wbf.add_weighted_attack("a", "b", -0.5).unwrap_err();
133 assert!(matches!(err, Error::InvalidWeight { .. }));
134 }
135
136 #[test]
137 fn self_support_rejected() {
138 let mut wbf = WeightedBipolarFramework::new();
139 let err = wbf.add_weighted_support("a", "a", 0.5).unwrap_err();
140 assert!(matches!(err, Error::IllegalSelfSupport));
141 }
142
143 #[test]
144 fn add_argument_idempotent() {
145 let mut wbf = WeightedBipolarFramework::new();
146 wbf.add_argument("a");
147 wbf.add_argument("a");
148 assert_eq!(wbf.argument_count(), 1);
149 }
150}