sdl_parser/
event.rs

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}