1use crate::context::{ContextKey, Fact, ProposedFact};
14
15#[derive(Debug, Default)]
22pub struct AgentEffect {
23 pub facts: Vec<Fact>,
25 pub proposals: Vec<ProposedFact>,
27 }
29
30impl AgentEffect {
31 #[must_use]
33 pub fn empty() -> Self {
34 Self::default()
35 }
36
37 #[must_use]
39 pub fn with_fact(fact: Fact) -> Self {
40 Self {
41 facts: vec![fact],
42 proposals: Vec::new(),
43 }
44 }
45
46 #[must_use]
48 pub fn with_facts(facts: Vec<Fact>) -> Self {
49 Self {
50 facts,
51 proposals: Vec::new(),
52 }
53 }
54
55 #[must_use]
57 pub fn with_proposal(proposal: ProposedFact) -> Self {
58 Self {
59 facts: Vec::new(),
60 proposals: vec![proposal],
61 }
62 }
63
64 #[must_use]
66 pub fn is_empty(&self) -> bool {
67 self.facts.is_empty() && self.proposals.is_empty()
68 }
69
70 #[must_use]
75 pub fn affected_keys(&self) -> Vec<ContextKey> {
76 let mut keys: Vec<ContextKey> = self
77 .facts
78 .iter()
79 .map(|f| f.key)
80 .chain(self.proposals.iter().map(|p| p.key))
81 .collect();
82 keys.sort();
83 keys.dedup();
84 keys
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91
92 #[test]
93 fn empty_effect_is_empty() {
94 let effect = AgentEffect::empty();
95 assert!(effect.is_empty());
96 }
97
98 #[test]
99 fn effect_with_fact_is_not_empty() {
100 let fact = Fact {
101 key: ContextKey::Seeds,
102 id: "test".into(),
103 content: "value".into(),
104 };
105 let effect = AgentEffect::with_fact(fact);
106 assert!(!effect.is_empty());
107 assert_eq!(effect.facts.len(), 1);
108 }
109
110 #[test]
111 fn effect_with_proposal_is_not_empty() {
112 let proposal = ProposedFact {
113 key: ContextKey::Hypotheses,
114 id: "prop-1".into(),
115 content: "value".into(),
116 confidence: 0.8,
117 provenance: "test".into(),
118 };
119 let effect = AgentEffect::with_proposal(proposal);
120 assert!(!effect.is_empty());
121 assert_eq!(effect.proposals.len(), 1);
122 }
123
124 #[test]
125 fn affected_keys_returns_unique_keys() {
126 let facts = vec![Fact {
127 key: ContextKey::Seeds,
128 id: "a".into(),
129 content: "1".into(),
130 }];
131 let proposals = vec![ProposedFact {
132 key: ContextKey::Hypotheses,
133 id: "c".into(),
134 content: "3".into(),
135 confidence: 0.9,
136 provenance: "test".into(),
137 }];
138 let mut effect = AgentEffect::with_facts(facts);
139 effect.proposals = proposals;
140 let keys = effect.affected_keys();
141 assert_eq!(keys.len(), 2);
142 assert!(keys.contains(&ContextKey::Seeds));
143 assert!(keys.contains(&ContextKey::Hypotheses));
144 }
145
146 #[test]
147 fn empty_effect_has_no_affected_keys() {
148 let effect = AgentEffect::empty();
149 assert!(effect.affected_keys().is_empty());
150 }
151}