1use std::fmt;
4
5use serde::de::{self, MapAccess, Visitor};
6use serde::ser::SerializeMap as _;
7use serde::{Deserialize, Deserializer, Serialize, Serializer};
8
9use super::Grants;
10
11#[derive(Clone, Debug, PartialEq, Eq, Hash)]
15pub struct EventPolicy {
16 pub send: Vec<String>,
18 pub receive: Vec<String>,
20}
21
22impl Default for EventPolicy {
23 fn default() -> EventPolicy {
24 EventPolicy::shared()
25 }
26}
27
28impl EventPolicy {
29 pub fn shared() -> EventPolicy {
31 EventPolicy {
32 send: vec!["*".into()],
33 receive: vec!["*".into()],
34 }
35 }
36
37 pub fn isolated() -> EventPolicy {
40 EventPolicy {
41 send: Vec::new(),
42 receive: Vec::new(),
43 }
44 }
45
46 pub fn new(send: Vec<String>, receive: Vec<String>) -> Result<EventPolicy, String> {
49 for pattern in send.iter().chain(&receive) {
50 let body = pattern.strip_suffix('*').unwrap_or(pattern);
51 if pattern.is_empty() || body.contains(['*', '?', '[', ']', '{', '}']) {
52 return Err(format!(
53 "invalid event channel pattern \"{pattern}\": use a channel name, a prefix ending in `*`, or `*`"
54 ));
55 }
56 }
57 Ok(EventPolicy { send, receive })
58 }
59
60 pub fn sends(&self, channel: &str) -> bool {
62 matches_any(&self.send, channel)
63 }
64
65 pub fn receives(&self, channel: &str) -> bool {
67 matches_any(&self.receive, channel)
68 }
69
70 pub fn intersection(&self, other: &EventPolicy) -> EventPolicy {
72 EventPolicy {
73 send: both(&self.send, &other.send),
74 receive: both(&self.receive, &other.receive),
75 }
76 }
77}
78
79fn both(left: &[String], right: &[String]) -> Vec<String> {
83 let covers = |patterns: &[String], pattern: &str| match pattern.strip_suffix('*') {
84 Some(prefix) => patterns.iter().any(|other| {
85 other
86 .strip_suffix('*')
87 .is_some_and(|wider| prefix.starts_with(wider))
88 }),
89 None => matches_any(patterns, pattern),
90 };
91 let mut found: Vec<String> = Vec::new();
92 for pattern in left.iter().filter(|pattern| covers(right, pattern)) {
93 found.push(pattern.clone());
94 }
95 for pattern in right.iter().filter(|pattern| covers(left, pattern)) {
96 if !found.contains(pattern) {
97 found.push(pattern.clone());
98 }
99 }
100 found
101}
102
103fn matches_any(patterns: &[String], channel: &str) -> bool {
104 patterns
105 .iter()
106 .any(|pattern| match pattern.strip_suffix('*') {
107 Some(prefix) => channel.starts_with(prefix),
108 None => pattern == channel,
109 })
110}
111
112pub fn delivers(
117 sender: (&Grants, &EventPolicy),
118 receiver: (&Grants, &EventPolicy),
119 channel: &str,
120) -> bool {
121 sender.0 == receiver.0 || sender.1.sends(channel) && receiver.1.receives(channel)
124}
125
126impl Serialize for EventPolicy {
129 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
130 if *self == EventPolicy::shared() {
131 return serializer.serialize_str("shared");
132 }
133 if *self == EventPolicy::isolated() {
134 return serializer.serialize_str("isolated");
135 }
136 let mut map = serializer.serialize_map(Some(2))?;
137 map.serialize_entry("send", &self.send)?;
138 map.serialize_entry("receive", &self.receive)?;
139 map.end()
140 }
141}
142
143impl<'de> Deserialize<'de> for EventPolicy {
146 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<EventPolicy, D::Error> {
147 struct PolicyVisitor;
148
149 impl<'de> Visitor<'de> for PolicyVisitor {
150 type Value = EventPolicy;
151
152 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
153 formatter.write_str("\"shared\", \"isolated\" or { send, receive }")
154 }
155
156 fn visit_str<E: de::Error>(self, value: &str) -> Result<EventPolicy, E> {
157 match value {
158 "shared" => Ok(EventPolicy::shared()),
159 "isolated" => Ok(EventPolicy::isolated()),
160 _ => Err(E::invalid_value(de::Unexpected::Str(value), &self)),
161 }
162 }
163
164 fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<EventPolicy, A::Error> {
165 let (mut send, mut receive) = (Vec::new(), Vec::new());
166 while let Some(key) = map.next_key::<String>()? {
167 match key.as_str() {
168 "send" => send = map.next_value()?,
169 "receive" => receive = map.next_value()?,
170 _ => return Err(de::Error::unknown_field(&key, &["send", "receive"])),
171 }
172 }
173 EventPolicy::new(send, receive).map_err(de::Error::custom)
174 }
175 }
176
177 deserializer.deserialize_any(PolicyVisitor)
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184
185 #[test]
186 fn event_policy_routing() {
187 let policy = |send: &[&str], receive: &[&str]| {
188 let owned = |patterns: &[&str]| patterns.iter().map(|p| (*p).to_owned()).collect();
189 EventPolicy::new(owned(send), owned(receive)).unwrap()
190 };
191 let todo = policy(&["todo:*"], &["subagent:done"]);
192 assert!(todo.sends("todo:changed") && todo.sends("todo:"));
193 assert!(!todo.sends("todos") && !todo.sends("subagent:done"));
194 assert!(todo.receives("subagent:done") && !todo.receives("subagent:done:x"));
195 assert!(EventPolicy::default().sends("any") && EventPolicy::shared().receives(""));
196 assert!(!EventPolicy::isolated().sends("any"));
197 let wide = policy(&["todo:*", "x"], &["*"]);
199 let both = todo.intersection(&wide);
200 assert_eq!(both, policy(&["todo:*"], &["subagent:done"]));
201 let narrow = policy(&["todo:a", "todo:b*", "y"], &[]);
202 assert_eq!(
203 narrow.intersection(&todo),
204 policy(&["todo:a", "todo:b*"], &[])
205 );
206 assert_eq!(
207 EventPolicy::shared().intersection(&EventPolicy::isolated()),
208 EventPolicy::isolated()
209 );
210
211 let fs = Grants {
212 filesystem: true,
213 ..Grants::none()
214 };
215 let all = Grants::default();
216 let isolated = EventPolicy::isolated();
217 let shared = EventPolicy::shared();
218 let cases = [
219 ((&fs, &isolated), (&fs, &isolated), "x", true),
221 ((&all, &shared), (&fs, &shared), "x", true),
222 ((&fs, &isolated), (&all, &shared), "x", false),
223 ((&all, &shared), (&fs, &isolated), "x", false),
224 ((&fs, &todo), (&all, &shared), "todo:changed", true),
225 ((&fs, &todo), (&all, &shared), "other", false),
226 ((&all, &shared), (&fs, &todo), "subagent:done", true),
227 ((&all, &shared), (&fs, &todo), "todo:changed", false),
228 ];
229 for (sender, receiver, channel, expected) in cases {
230 assert_eq!(
231 delivers(sender, receiver, channel),
232 expected,
233 "{sender:?} to {receiver:?} on {channel}"
234 );
235 }
236
237 for bad in ["", "a*b", "**", "todo:?", "[ab]", "{a,b}"] {
238 assert!(
239 EventPolicy::new(vec![bad.into()], Vec::new()).is_err(),
240 "{bad}"
241 );
242 }
243 let parse =
244 |json: &str| serde_json::from_str::<EventPolicy>(json).map_err(|e| e.to_string());
245 assert_eq!(parse("\"shared\""), Ok(EventPolicy::shared()));
246 assert_eq!(parse("\"isolated\""), Ok(EventPolicy::isolated()));
247 assert_eq!(
248 parse(r#"{"send": ["todo:*"]}"#),
249 Ok(policy(&["todo:*"], &[]))
250 );
251 for bad in [r#""open""#, r#"{"sent": []}"#, r#"{"send": ["a*b"]}"#, "[]"] {
252 assert!(parse(bad).is_err(), "{bad}");
253 }
254 for policy in [EventPolicy::shared(), EventPolicy::isolated(), todo] {
255 let json = yapi_types::json::to_string(&policy).unwrap();
256 assert_eq!(parse(&json), Ok(policy));
257 }
258 }
259}