1use serde::ser::SerializeSeq as _;
6use std::str::FromStr;
7
8#[derive(serde::Deserialize, serde::Serialize, Debug)]
9pub struct Expectations {
10 #[serde(rename = "actions")]
11 pub expectations: Vec<Expectation>,
12 pub cases_to_run: CasesToRun,
13}
14
15pub const ALL_CASES_LABEL: &str = "All";
16const WITH_ERR_LOGS_CASES_LABEL: &str = "WithErrLogs";
17const NO_ERR_LOGS_CASES_LABEL: &str = "NoErrLogs";
18
19#[derive(serde::Deserialize, serde::Serialize, Debug, Eq, PartialEq)]
20#[serde(tag = "type", rename_all = "snake_case")]
21pub enum CasesToRun {
22 WithErrLogs,
23 NoErrLogs,
24 All,
25}
26
27impl FromStr for CasesToRun {
28 type Err = anyhow::Error;
29
30 fn from_str(s: &str) -> Result<Self, Self::Err> {
31 match s {
32 NO_ERR_LOGS_CASES_LABEL => Ok(CasesToRun::NoErrLogs),
33 WITH_ERR_LOGS_CASES_LABEL => Ok(CasesToRun::WithErrLogs),
34 ALL_CASES_LABEL => Ok(CasesToRun::All),
35 _ => Err(anyhow::anyhow!("Invalid CasesToRun {}", s)),
36 }
37 }
38}
39
40#[derive(Clone, serde::Deserialize, serde::Serialize, Debug)]
41pub struct Matchers {
42 #[serde(deserialize_with = "deserialize_glob_vec", serialize_with = "serialize_glob_vec")]
43 pub matchers: Vec<glob::Pattern>,
44}
45
46fn deserialize_glob_vec<'de, D>(deserializer: D) -> Result<Vec<glob::Pattern>, D::Error>
47where
48 D: serde::Deserializer<'de>,
49{
50 let strings: Vec<String> = serde::Deserialize::deserialize(deserializer)?;
51 strings.into_iter().map(|s| s.parse().map_err(serde::de::Error::custom)).collect()
52}
53
54fn serialize_glob_vec<S>(globs: &[glob::Pattern], serializer: S) -> Result<S::Ok, S::Error>
55where
56 S: serde::Serializer,
57{
58 let mut seq = serializer.serialize_seq(Some(globs.len()))?;
59 for glob in globs {
60 seq.serialize_element(glob.as_str())?;
61 }
62 seq.end()
63}
64
65#[derive(serde::Deserialize, serde::Serialize, Debug)]
66#[serde(tag = "type", rename_all = "snake_case")]
67pub enum Expectation {
68 ExpectFailure(Matchers),
69 ExpectPass(Matchers),
70 Skip(Matchers),
71 ExpectFailureWithErrLogs(Matchers),
72 ExpectPassWithErrLogs(Matchers),
73}
74
75impl Expectation {
76 pub fn matchers(&self) -> &[glob::Pattern] {
77 match self {
78 Expectation::ExpectFailure(matchers)
79 | Expectation::ExpectPass(matchers)
80 | Expectation::Skip(matchers)
81 | Expectation::ExpectFailureWithErrLogs(matchers)
82 | Expectation::ExpectPassWithErrLogs(matchers) => {
83 let Matchers { matchers } = matchers;
84 matchers
85 }
86 }
87 }
88}
89
90#[derive(serde::Deserialize, serde::Serialize, Debug)]
91pub struct Include {
92 #[serde(rename = "include")]
93 pub path: String,
94}
95
96#[derive(serde::Deserialize, serde::Serialize, Debug)]
97#[serde(untagged)]
98pub enum UnmergedExpectation {
99 Include(Include),
100 Expectation(Expectation),
101}
102
103#[derive(serde::Deserialize, serde::Serialize, Debug)]
104pub struct UnmergedExpectations {
105 #[serde(rename = "actions")]
106 pub expectations: Vec<UnmergedExpectation>,
107}
108
109#[cfg(test)]
110mod tests {
111 use crate::{CasesToRun, ALL_CASES_LABEL, NO_ERR_LOGS_CASES_LABEL, WITH_ERR_LOGS_CASES_LABEL};
112 use std::str::FromStr;
113
114 #[test]
115 fn cases_to_run_from_str() {
116 assert_eq!(CasesToRun::from_str(ALL_CASES_LABEL).unwrap(), CasesToRun::All);
117 assert_eq!(
118 CasesToRun::from_str(WITH_ERR_LOGS_CASES_LABEL).unwrap(),
119 CasesToRun::WithErrLogs
120 );
121 assert_eq!(CasesToRun::from_str(NO_ERR_LOGS_CASES_LABEL).unwrap(), CasesToRun::NoErrLogs);
122 assert!(CasesToRun::from_str("Garbage").is_err());
123 }
124}