use rootcause::{Report, report};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::fmt::{Debug, Display};
use std::str::FromStr;
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
#[error("Invalid channel: '{value}'. Channel names must not be empty.")]
pub struct ParseChannelError {
value: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Channel {
Stable,
Beta,
Dev,
Canary,
Other(String),
}
impl Serialize for Channel {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.as_str())
}
}
impl<'de> Deserialize<'de> for Channel {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
String::deserialize(deserializer)?
.parse()
.map_err(serde::de::Error::custom)
}
}
impl Display for Channel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
Channel::Stable => "Stable",
Channel::Beta => "Beta",
Channel::Dev => "Dev",
Channel::Canary => "Canary",
Channel::Other(name) => name,
})
}
}
impl FromStr for Channel {
type Err = Report<ParseChannelError>;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"Stable" | "stable" => Ok(Channel::Stable),
"Beta" | "beta" => Ok(Channel::Beta),
"Dev" | "dev" => Ok(Channel::Dev),
"Canary" | "canary" => Ok(Channel::Canary),
"" => Err(report!(ParseChannelError {
value: s.to_owned(),
})),
name => Ok(Channel::Other(name.to_owned())),
}
}
}
impl Channel {
#[must_use]
pub fn is_known(&self) -> bool {
match self {
Channel::Stable | Channel::Beta | Channel::Dev | Channel::Canary => true,
Channel::Other(_) => false,
}
}
#[must_use]
pub fn as_str(&self) -> &str {
match self {
Channel::Stable => "Stable",
Channel::Beta => "Beta",
Channel::Dev => "Dev",
Channel::Canary => "Canary",
Channel::Other(name) => name,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use assertr::prelude::*;
fn channels() -> [(&'static str, Channel); 10] {
[
("Stable", Channel::Stable),
("stable", Channel::Stable),
("Beta", Channel::Beta),
("beta", Channel::Beta),
("Dev", Channel::Dev),
("dev", Channel::Dev),
("Canary", Channel::Canary),
("canary", Channel::Canary),
("Unknown", Channel::Other("Unknown".to_owned())),
("unknown", Channel::Other("unknown".to_owned())),
]
}
#[test]
fn parse_empty_string_fails() {
let err = "".parse::<Channel>().unwrap_err();
assert_that!(err.current_context()).is_equal_to(ParseChannelError {
value: String::new(),
});
}
#[test]
fn parse_channels() {
for (test, expected) in channels() {
assert_that!(test.parse::<Channel>())
.is_ok()
.is_equal_to(expected.clone());
}
}
#[test]
fn deserialize_channels() {
for (test, expected) in channels() {
assert_that!(serde_json::from_str::<Channel>(&format!(r#""{test}""#)))
.is_ok()
.is_equal_to(expected);
}
}
#[test]
fn serialized_channels() {
fn capitalize_first(s: &str) -> String {
s.chars()
.take(1)
.flat_map(|f| f.to_uppercase())
.chain(s.chars().skip(1))
.collect()
}
for (expected, channel) in channels() {
assert_that!(serde_json::to_string(&channel))
.is_ok()
.is_equal_to(format!(
r#""{}""#,
match channel {
Channel::Other(_) => expected.to_owned(),
_ => capitalize_first(expected),
}
));
}
}
}