telltale_language/ast/
annotation_collection.rs1use super::ProtocolAnnotation;
2use std::collections::HashMap;
3use std::time::Duration;
4
5#[derive(Debug, Clone, Default, PartialEq)]
7pub struct Annotations {
8 items: Vec<ProtocolAnnotation>,
9}
10
11impl Annotations {
12 #[must_use]
14 pub fn new() -> Self {
15 Self { items: Vec::new() }
16 }
17
18 #[must_use]
20 pub fn single(annotation: ProtocolAnnotation) -> Self {
21 Self {
22 items: vec![annotation],
23 }
24 }
25
26 #[must_use]
28 pub fn from_vec(items: Vec<ProtocolAnnotation>) -> Self {
29 Self { items }
30 }
31
32 pub fn from_dsl_map(map: &HashMap<String, String>) -> Self {
33 let mut items = Vec::new();
34
35 if map.get("timed_choice").is_some_and(|v| v == "true") {
36 let duration = map
37 .get("timeout_ms")
38 .and_then(|v| v.parse::<u64>().ok())
39 .map(Duration::from_millis)
40 .unwrap_or_else(|| Duration::from_secs(5));
41 items.push(ProtocolAnnotation::TimedChoice { duration });
42 }
43
44 let mut entries: Vec<_> = map.iter().collect();
45 entries.sort_by(|(key_a, _), (key_b, _)| key_a.cmp(key_b));
46 for (key, value) in entries {
47 if key == "timed_choice" || key == "timeout_ms" {
48 continue;
49 }
50 items.push(ProtocolAnnotation::parse_dsl_entry(key, value));
51 }
52
53 Self { items }
54 }
55
56 pub fn dsl_map(&self) -> HashMap<String, String> {
57 let mut map = HashMap::new();
58
59 for annotation in &self.items {
60 for (key, value) in annotation.dsl_entries() {
61 map.insert(key, value);
62 }
63 }
64
65 map
66 }
67
68 pub fn push(&mut self, annotation: ProtocolAnnotation) {
70 self.items.push(annotation);
71 }
72
73 #[must_use]
75 pub fn is_empty(&self) -> bool {
76 self.items.is_empty()
77 }
78
79 #[must_use]
81 pub fn len(&self) -> usize {
82 self.items.len()
83 }
84
85 pub fn iter(&self) -> impl Iterator<Item = &ProtocolAnnotation> {
87 self.items.iter()
88 }
89
90 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut ProtocolAnnotation> {
92 self.items.iter_mut()
93 }
94
95 #[must_use]
97 pub fn any<F>(&self, f: F) -> bool
98 where
99 F: FnMut(&ProtocolAnnotation) -> bool,
100 {
101 self.items.iter().any(f)
102 }
103
104 #[must_use]
106 pub fn find<F>(&self, f: F) -> Option<&ProtocolAnnotation>
107 where
108 F: FnMut(&&ProtocolAnnotation) -> bool,
109 {
110 self.items.iter().find(f)
111 }
112
113 #[must_use]
115 pub fn timed_choice(&self) -> Option<Duration> {
116 self.items.iter().find_map(|a| a.timed_choice_duration())
117 }
118
119 #[must_use]
121 pub fn has_timed_choice(&self) -> bool {
122 self.items.iter().any(|a| a.is_timed_choice())
123 }
124
125 #[must_use]
127 pub fn priority(&self) -> Option<u32> {
128 self.items.iter().find_map(|a| a.priority_value())
129 }
130
131 #[must_use]
133 pub fn is_idempotent(&self) -> bool {
134 self.items.iter().any(|a| a.is_idempotent())
135 }
136
137 #[must_use]
139 pub fn has_trace(&self) -> bool {
140 self.items.iter().any(|a| a.is_trace())
141 }
142
143 #[must_use]
145 pub fn has_heartbeat(&self) -> bool {
146 self.items.iter().any(|a| a.is_heartbeat())
147 }
148
149 #[must_use]
151 pub fn heartbeat(&self) -> Option<(Duration, u32)> {
152 self.items.iter().find_map(|a| a.heartbeat_params())
153 }
154
155 #[must_use]
157 pub fn has_runtime_timeout(&self) -> bool {
158 self.items.iter().any(|a| a.is_runtime_timeout())
159 }
160
161 #[must_use]
163 pub fn runtime_timeout(&self) -> Option<Duration> {
164 self.items.iter().find_map(|a| a.runtime_timeout_duration())
165 }
166
167 #[must_use]
169 pub fn has_parallel(&self) -> bool {
170 self.items.iter().any(|a| a.is_parallel())
171 }
172
173 #[must_use]
175 pub fn has_ordered(&self) -> bool {
176 self.items.iter().any(|a| a.is_ordered())
177 }
178
179 #[must_use]
181 pub fn has_min_responses(&self) -> bool {
182 self.items.iter().any(|a| a.is_min_responses())
183 }
184
185 #[must_use]
187 pub fn min_responses(&self) -> Option<u32> {
188 self.items.iter().find_map(|a| a.min_responses_value())
189 }
190
191 #[must_use]
193 pub fn custom(&self, key: &str) -> Option<&str> {
194 self.items.iter().find_map(|a| a.custom_value(key))
195 }
196
197 pub fn merge(&mut self, other: &Annotations) {
199 self.items.extend(other.items.iter().cloned());
200 }
201
202 pub fn clear(&mut self) {
204 self.items.clear();
205 }
206}
207
208impl IntoIterator for Annotations {
209 type Item = ProtocolAnnotation;
210 type IntoIter = std::vec::IntoIter<ProtocolAnnotation>;
211
212 fn into_iter(self) -> Self::IntoIter {
213 self.items.into_iter()
214 }
215}
216
217impl<'a> IntoIterator for &'a Annotations {
218 type Item = &'a ProtocolAnnotation;
219 type IntoIter = std::slice::Iter<'a, ProtocolAnnotation>;
220
221 fn into_iter(self) -> Self::IntoIter {
222 self.items.iter()
223 }
224}
225
226impl FromIterator<ProtocolAnnotation> for Annotations {
227 fn from_iter<I: IntoIterator<Item = ProtocolAnnotation>>(iter: I) -> Self {
228 Self {
229 items: iter.into_iter().collect(),
230 }
231 }
232}
233
234#[cfg(test)]
235mod tests {
236 use super::*;
237
238 #[test]
239 fn test_timed_choice_annotation() {
240 let ann = ProtocolAnnotation::timed_choice_ms(5000);
241 assert!(ann.is_timed_choice());
242 assert_eq!(ann.timed_choice_duration(), Some(Duration::from_secs(5)));
243 }
244
245 #[test]
246 fn test_priority_annotation() {
247 let ann = ProtocolAnnotation::priority(10);
248 assert!(ann.is_priority());
249 assert_eq!(ann.priority_value(), Some(10));
250 }
251
252 #[test]
253 fn test_custom_annotation() {
254 let ann = ProtocolAnnotation::custom("my_key", "my_value");
255 assert!(ann.is_custom_key("my_key"));
256 assert_eq!(ann.custom_value("my_key"), Some("my_value"));
257 assert_eq!(ann.custom_value("other"), None);
258 }
259
260 #[test]
261 fn test_parse_dsl_entry() {
262 let ann = ProtocolAnnotation::parse_dsl_entry("priority", "5");
263 assert_eq!(ann, ProtocolAnnotation::Priority(5));
264
265 let ann = ProtocolAnnotation::parse_dsl_entry("unknown", "value");
266 assert!(matches!(ann, ProtocolAnnotation::Custom { .. }));
267 }
268
269 #[test]
270 fn test_annotations_collection() {
271 let mut anns = Annotations::new();
272 anns.push(ProtocolAnnotation::timed_choice_ms(1000));
273 anns.push(ProtocolAnnotation::priority(5));
274
275 assert!(anns.has_timed_choice());
276 assert_eq!(anns.timed_choice(), Some(Duration::from_secs(1)));
277 assert_eq!(anns.priority(), Some(5));
278 assert_eq!(anns.len(), 2);
279 }
280
281 #[test]
282 fn test_map_roundtrip() {
283 let mut original = HashMap::new();
284 original.insert("timed_choice".to_string(), "true".to_string());
285 original.insert("timeout_ms".to_string(), "5000".to_string());
286 original.insert("priority".to_string(), "10".to_string());
287
288 let anns = Annotations::from_dsl_map(&original);
289 assert!(anns.has_timed_choice());
290 assert_eq!(anns.timed_choice(), Some(Duration::from_secs(5)));
291 assert_eq!(anns.priority(), Some(10));
292
293 let restored = anns.dsl_map();
294 assert_eq!(restored.get("timed_choice"), Some(&"true".to_string()));
295 assert_eq!(restored.get("timeout_ms"), Some(&"5000".to_string()));
296 }
297
298 #[test]
299 fn test_parallel_annotation() {
300 let ann = ProtocolAnnotation::parallel();
301 assert!(ann.is_parallel());
302 assert!(!ann.is_ordered());
303 }
304
305 #[test]
306 fn test_ordered_annotation() {
307 let ann = ProtocolAnnotation::ordered();
308 assert!(ann.is_ordered());
309 assert!(!ann.is_parallel());
310 }
311
312 #[test]
313 fn test_min_responses_annotation() {
314 let ann = ProtocolAnnotation::min_responses(3);
315 assert!(ann.is_min_responses());
316 assert_eq!(ann.min_responses_value(), Some(3));
317 }
318
319 #[test]
320 fn test_annotations_parallel_ordered() {
321 let mut anns = Annotations::new();
322 anns.push(ProtocolAnnotation::parallel());
323 anns.push(ProtocolAnnotation::min_responses(5));
324
325 assert!(anns.has_parallel());
326 assert!(!anns.has_ordered());
327 assert!(anns.has_min_responses());
328 assert_eq!(anns.min_responses(), Some(5));
329 }
330
331 #[test]
332 fn test_parse_dsl_entry_parallel() {
333 let ann = ProtocolAnnotation::parse_dsl_entry("parallel", "true");
334 assert_eq!(ann, ProtocolAnnotation::Parallel);
335
336 let ann = ProtocolAnnotation::parse_dsl_entry("ordered", "true");
337 assert_eq!(ann, ProtocolAnnotation::Ordered);
338
339 let ann = ProtocolAnnotation::parse_dsl_entry("parallel", "");
340 assert_eq!(ann, ProtocolAnnotation::Parallel);
341
342 let ann = ProtocolAnnotation::parse_dsl_entry("ordered", "");
343 assert_eq!(ann, ProtocolAnnotation::Ordered);
344
345 let ann = ProtocolAnnotation::parse_dsl_entry("min_responses", "3");
346 assert_eq!(ann, ProtocolAnnotation::MinResponses(3));
347 }
348
349 #[test]
350 fn test_to_map_new_annotations() {
351 let mut anns = Annotations::new();
352 anns.push(ProtocolAnnotation::Parallel);
353 anns.push(ProtocolAnnotation::Ordered);
354 anns.push(ProtocolAnnotation::MinResponses(5));
355
356 let map = anns.dsl_map();
357 assert_eq!(map.get("parallel"), Some(&"true".to_string()));
358 assert_eq!(map.get("ordered"), Some(&"true".to_string()));
359 assert_eq!(map.get("min_responses"), Some(&"5".to_string()));
360 }
361}