ser/
lib.rs

1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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}