1use anyhow::{anyhow, Result};
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4
5use crate::{
6 common::{HelperSource, Source},
7 condition::Condition,
8 helpers::Connection,
9 inject::Inject,
10 Formalize,
11};
12
13#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
14pub struct Event {
15 #[serde(default, alias = "Name", alias = "NAME")]
16 pub name: Option<String>,
17 #[serde(
18 default,
19 rename = "source",
20 alias = "Source",
21 alias = "SOURCE",
22 skip_serializing
23 )]
24 _source_helper: Option<HelperSource>,
25 #[serde(default, skip_deserializing)]
26 pub source: Option<Source>,
27 #[serde(alias = "Conditions", alias = "CONDITIONS")]
28 pub conditions: Option<Vec<String>>,
29 #[serde(alias = "Injects", alias = "INJECTS")]
30 pub injects: Option<Vec<String>>,
31 #[serde(alias = "Description", alias = "DESCRIPTION")]
32 pub description: Option<String>,
33}
34
35pub type Events = HashMap<String, Event>;
36
37impl Formalize for Event {
38 fn formalize(&mut self) -> Result<()> {
39 if self._source_helper.is_some() {
40 if let Some(helper_source) = &self._source_helper {
41 self.source = Some(helper_source.to_owned().into());
42 } else {
43 return Err(anyhow!("Event missing Source field"));
44 }
45 }
46
47 Ok(())
48 }
49}
50impl Connection<Condition> for (&String, &Event) {
51 fn validate_connections(&self, potential_condition_names: &Option<Vec<String>>) -> Result<()> {
52 if self.1.conditions.is_some() && potential_condition_names.is_none() {
53 return Err(anyhow!(
54 "Event \"{event_name}\" has Conditions but none found under Scenario",
55 event_name = self.0
56 ));
57 }
58
59 if let Some(required_conditions) = &self.1.conditions {
60 if let Some(condition_names) = potential_condition_names {
61 for event_condition_name in required_conditions {
62 if !condition_names.contains(event_condition_name) {
63 return Err(anyhow!(
64 "Condition \"{event_condition_name}\" not found under Scenario"
65 ));
66 }
67 }
68 }
69 }
70
71 Ok(())
72 }
73}
74
75impl Connection<Inject> for (&String, &Event) {
76 fn validate_connections(&self, potential_inject_names: &Option<Vec<String>>) -> Result<()> {
77 if self.1.injects.is_some() && potential_inject_names.is_none() {
78 return Err(anyhow!(
79 "Event \"{event_name}\" has Injects but none found under Scenario",
80 event_name = self.0
81 ));
82 }
83
84 if let Some(required_injects) = &self.1.injects {
85 if let Some(inject_names) = potential_inject_names {
86 for event_inject_name in required_injects {
87 if !inject_names.contains(event_inject_name) {
88 return Err(anyhow!(
89 "Inject \"{event_inject_name}\" not found under Scenario",
90 event_inject_name = event_inject_name
91 ));
92 }
93 }
94 }
95 }
96
97 Ok(())
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104 use crate::parse_sdl;
105
106 #[test]
107 fn parses_sdl_with_events() {
108 let sdl = r#"
109 name: test-scenario
110 description: some description
111 conditions:
112 condition-1:
113 command: executable/path.sh
114 interval: 30
115 injects:
116 my-cool-inject:
117 source: inject-package
118 events:
119 my-cool-event:
120 source: event-package
121 conditions:
122 - condition-1
123 injects:
124 - my-cool-inject
125 "#;
126 let schema = parse_sdl(sdl).unwrap();
127
128 insta::with_settings!({sort_maps => true}, {
129 insta::assert_yaml_snapshot!(schema);
130 });
131 }
132
133 #[test]
134 fn parses_single_event() {
135 let event = r#"
136 source:
137 name: event-package
138 version: 1.0.0
139 conditions:
140 - condition-1
141 injects:
142 - my-cool-inject
143 "#;
144 serde_yaml::from_str::<Event>(event).unwrap();
145 }
146
147 #[test]
148 #[should_panic(expected = "Condition \"condition-1\" not found under Scenario")]
149 fn fails_on_missing_scenario_condition() {
150 let sdl = r#"
151 name: test-scenario
152 description: some description
153 conditions:
154 condition-3000:
155 source: digital-library-package
156 injects:
157 my-cool-inject:
158 source: inject-package
159 events:
160 my-cool-event:
161 conditions:
162 - condition-1
163 injects:
164 - my-cool-inject
165 "#;
166 parse_sdl(sdl).unwrap();
167 }
168
169 #[test]
170 #[should_panic(
171 expected = "Event \"my-cool-event\" has Conditions but none found under Scenario"
172 )]
173 fn fails_on_missing_conditions() {
174 let sdl = r#"
175 name: test-scenario
176 description: some description
177 injects:
178 my-cool-inject:
179 source: inject-package
180 events:
181 my-cool-event:
182 conditions:
183 - condition-1
184 injects:
185 - my-cool-inject
186 "#;
187 parse_sdl(sdl).unwrap();
188 }
189
190 #[test]
191 #[should_panic(expected = "Event \"my-cool-event\" has Injects but none found under Scenario")]
192 fn fails_no_injects_under_scenario() {
193 let sdl = r#"
194 name: test-scenario
195 description: some description
196 events:
197 my-cool-event:
198 injects:
199 - my-cool-inject
200 "#;
201 parse_sdl(sdl).unwrap();
202 }
203
204 #[test]
205 #[should_panic(expected = "Inject \"my-cool-inject\" not found under Scenario")]
206 fn fails_on_missing_inject() {
207 let sdl = r#"
208 name: test-scenario
209 description: some description
210 conditions:
211 condition-1:
212 command: executable/path.sh
213 interval: 30
214 injects:
215 inject-1:
216 source: inject-package
217 events:
218 my-cool-event:
219 injects:
220 - my-cool-inject
221 "#;
222 parse_sdl(sdl).unwrap();
223 }
224}