Skip to main content

chrome_for_testing/api/
channel.rs

1use rootcause::{Report, report};
2use serde::{Deserialize, Deserializer, Serialize, Serializer};
3use std::fmt::{Debug, Display};
4use std::str::FromStr;
5
6/// Error returned when parsing a channel string fails.
7#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
8#[error("Invalid channel: '{value}'. Channel names must not be empty.")]
9pub struct ParseChannelError {
10    value: String,
11}
12
13/// Chrome release channel.
14#[derive(Debug, Clone, PartialEq, Eq, Hash)]
15pub enum Channel {
16    /// The stable release channel - the default Chrome version for general users.
17    Stable,
18
19    /// The beta release channel - preview of upcoming stable features.
20    /// Less stable than stable, used for testing new functionality before it reaches stable.
21    Beta,
22
23    /// The dev release channel - early development builds with cutting-edge features.
24    /// Less stable than beta, intended for developers and early adopters.
25    Dev,
26
27    /// The canary release channel - nightly builds with the absolute latest changes.
28    /// Less stable than dev, highly experimental.
29    Canary,
30
31    /// An upstream channel name this crate does not know yet.
32    Other(String),
33}
34
35impl Serialize for Channel {
36    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
37    where
38        S: Serializer,
39    {
40        serializer.serialize_str(self.as_str())
41    }
42}
43
44impl<'de> Deserialize<'de> for Channel {
45    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
46    where
47        D: Deserializer<'de>,
48    {
49        String::deserialize(deserializer)?
50            .parse()
51            .map_err(serde::de::Error::custom)
52    }
53}
54
55impl Display for Channel {
56    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57        f.write_str(match self {
58            Channel::Stable => "Stable",
59            Channel::Beta => "Beta",
60            Channel::Dev => "Dev",
61            Channel::Canary => "Canary",
62            Channel::Other(name) => name,
63        })
64    }
65}
66
67impl FromStr for Channel {
68    type Err = Report<ParseChannelError>;
69
70    fn from_str(s: &str) -> Result<Self, Self::Err> {
71        match s {
72            "Stable" | "stable" => Ok(Channel::Stable),
73            "Beta" | "beta" => Ok(Channel::Beta),
74            "Dev" | "dev" => Ok(Channel::Dev),
75            "Canary" | "canary" => Ok(Channel::Canary),
76            "" => Err(report!(ParseChannelError {
77                value: s.to_owned(),
78            })),
79            name => Ok(Channel::Other(name.to_owned())),
80        }
81    }
82}
83
84impl Channel {
85    /// Returns whether this is one of the four channel names currently documented by Chrome for
86    /// Testing.
87    #[must_use]
88    pub fn is_known(&self) -> bool {
89        match self {
90            Channel::Stable | Channel::Beta | Channel::Dev | Channel::Canary => true,
91            Channel::Other(_) => false,
92        }
93    }
94
95    /// Returns the raw upstream channel name.
96    #[must_use]
97    pub fn as_str(&self) -> &str {
98        match self {
99            Channel::Stable => "Stable",
100            Channel::Beta => "Beta",
101            Channel::Dev => "Dev",
102            Channel::Canary => "Canary",
103            Channel::Other(name) => name,
104        }
105    }
106}
107
108#[cfg(test)]
109mod tests {
110    use super::*;
111    use assertr::prelude::*;
112
113    fn channels() -> [(&'static str, Channel); 10] {
114        [
115            ("Stable", Channel::Stable),
116            ("stable", Channel::Stable),
117            ("Beta", Channel::Beta),
118            ("beta", Channel::Beta),
119            ("Dev", Channel::Dev),
120            ("dev", Channel::Dev),
121            ("Canary", Channel::Canary),
122            ("canary", Channel::Canary),
123            ("Unknown", Channel::Other("Unknown".to_owned())),
124            ("unknown", Channel::Other("unknown".to_owned())),
125        ]
126    }
127
128    #[test]
129    fn parse_empty_string_fails() {
130        let err = "".parse::<Channel>().unwrap_err();
131
132        assert_that!(err.current_context()).is_equal_to(ParseChannelError {
133            value: String::new(),
134        });
135    }
136
137    #[test]
138    fn parse_channels() {
139        for (test, expected) in channels() {
140            assert_that!(test.parse::<Channel>())
141                .is_ok()
142                .is_equal_to(expected.clone());
143        }
144    }
145
146    #[test]
147    fn deserialize_channels() {
148        for (test, expected) in channels() {
149            assert_that!(serde_json::from_str::<Channel>(&format!(r#""{test}""#)))
150                .is_ok()
151                .is_equal_to(expected);
152        }
153    }
154
155    #[test]
156    fn serialized_channels() {
157        fn capitalize_first(s: &str) -> String {
158            s.chars()
159                .take(1)
160                .flat_map(|f| f.to_uppercase())
161                .chain(s.chars().skip(1))
162                .collect()
163        }
164
165        for (expected, channel) in channels() {
166            assert_that!(serde_json::to_string(&channel))
167                .is_ok()
168                .is_equal_to(format!(
169                    r#""{}""#,
170                    match channel {
171                        Channel::Other(_) => expected.to_owned(),
172                        _ => capitalize_first(expected),
173                    }
174                ));
175        }
176    }
177}