sdl_parser/
condition.rs

1use anyhow::{Ok, Result};
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4
5use crate::{
6    common::{HelperSource, Source},
7    Formalize,
8};
9
10#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)]
11pub struct Condition {
12    #[serde(default, alias = "Name", alias = "NAME")]
13    pub name: Option<String>,
14    #[serde(default, alias = "Command", alias = "COMMAND")]
15    pub command: Option<String>,
16    #[serde(default, alias = "Interval", alias = "INTERVAL")]
17    pub interval: Option<u32>,
18    #[serde(
19        default,
20        rename = "source",
21        alias = "Source",
22        alias = "SOURCE",
23        skip_serializing
24    )]
25    source_helper: Option<HelperSource>,
26    #[serde(default, skip_deserializing)]
27    pub source: Option<Source>,
28    #[serde(alias = "Description", alias = "DESCRIPTION")]
29    pub description: Option<String>,
30    #[serde(alias = "Environment", alias = "ENVIRONMENT")]
31    pub environment: Option<Vec<String>>,
32}
33
34impl Formalize for Condition {
35    fn formalize(&mut self) -> Result<()> {
36        if let Some(source_helper) = &self.source_helper {
37            self.source = Some(source_helper.to_owned().into());
38        }
39
40        let has_command = self.command.is_some();
41        let has_interval = self.interval.is_some();
42        let has_source = self.source.is_some();
43
44        if has_source && (has_command || has_interval) {
45            return Err(anyhow::anyhow!(
46                "Condition must have Command and Interval or Source defined, not both"
47            ));
48        } else if has_command && !has_interval {
49            return Err(anyhow::anyhow!(
50                "Condition has Command defined but is missing Interval"
51            ));
52        } else if !has_command && has_interval {
53            return Err(anyhow::anyhow!(
54                "Condition has Interval defined but is missing Command"
55            ));
56        }
57        Ok(())
58    }
59}
60
61pub type Conditions = HashMap<String, Condition>;
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66    use crate::parse_sdl;
67
68    #[test]
69    fn conditions_are_mapped_correctly() {
70        let sdl = r#"
71            name: test-scenario
72            description: some-description
73            conditions:
74                condition-1:
75                    command: executable/path.sh
76                    interval: 30
77                condition-2:
78                    source: digital-library-package
79        "#;
80        let conditions = parse_sdl(sdl).unwrap().conditions;
81        insta::with_settings!({sort_maps => true}, {
82                insta::assert_yaml_snapshot!(conditions);
83        });
84    }
85
86    #[test]
87    fn handles_metrics_with_conditions_correctly() {
88        let sdl = r#"
89            name: test-scenario
90            description: some-description
91            conditions:
92                condition-1:
93                    command: executable/path.sh
94                    interval: 30
95                    description: This is a description for condition 1
96                condition-2:
97                    source: digital-library-package
98                    description: This is a description for condition 2
99            metrics:
100                metric-1:
101                    type: MANUAL
102                    artifact: true
103                    max-score: 10
104                metric-2:
105                    type: CONDITIONAL
106                    max-score: 10
107                    condition:  condition-2
108        "#;
109        let conditions = parse_sdl(sdl).unwrap().conditions;
110        insta::with_settings!({sort_maps => true}, {
111                insta::assert_yaml_snapshot!(conditions);
112        });
113    }
114
115    #[test]
116    #[should_panic(expected = "Condition \"condition-3\" not found under Scenario Conditions")]
117    fn identifies_missing_condition() {
118        let sdl = r#"
119            name: test-scenario
120            description: some-description
121            conditions:
122                condition-1:
123                    command: executable/path.sh
124                    interval: 30
125                    description: This is a description for condition 1
126                condition-2:
127                    source: digital-library-package
128                    description: This is a description for condition 2
129            metrics:
130                metric-1:
131                    type: MANUAL
132                    artifact: true
133                    max-score: 10
134                metric-2:
135                    type: CONDITIONAL
136                    max-score: 10
137                    condition:  condition-3
138        "#;
139        parse_sdl(sdl).unwrap();
140    }
141
142    #[test]
143    fn command_condition_is_parsed() {
144        let sdl = r#"
145            command: executable/path.sh
146            interval: 30       
147
148        "#;
149        let condition = serde_yaml::from_str::<Condition>(sdl).unwrap();
150        insta::assert_debug_snapshot!(condition);
151    }
152
153    #[test]
154    fn library_condition_is_parsed() {
155        let sdl = r#"
156            source: digital-library-package 
157
158        "#;
159        let condition = serde_yaml::from_str::<Condition>(sdl).unwrap();
160        insta::assert_debug_snapshot!(condition);
161    }
162}