use std::fmt;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Direction {
None,
TripleUp,
DoubleUp,
SingleUp,
FortyFiveUp,
Flat,
FortyFiveDown,
SingleDown,
DoubleDown,
TripleDown,
NotComputable,
RateOutOfRange,
Unknown(String),
}
impl Direction {
#[must_use]
pub fn parse(s: &str) -> Self {
match s {
"NONE" => Self::None,
"TripleUp" => Self::TripleUp,
"DoubleUp" => Self::DoubleUp,
"SingleUp" => Self::SingleUp,
"FortyFiveUp" => Self::FortyFiveUp,
"Flat" => Self::Flat,
"FortyFiveDown" => Self::FortyFiveDown,
"SingleDown" => Self::SingleDown,
"DoubleDown" => Self::DoubleDown,
"TripleDown" => Self::TripleDown,
"NOT COMPUTABLE" => Self::NotComputable,
"RATE OUT OF RANGE" => Self::RateOutOfRange,
other => Self::Unknown(other.to_owned()),
}
}
#[must_use]
pub fn as_str(&self) -> &str {
match self {
Self::None => "NONE",
Self::TripleUp => "TripleUp",
Self::DoubleUp => "DoubleUp",
Self::SingleUp => "SingleUp",
Self::FortyFiveUp => "FortyFiveUp",
Self::Flat => "Flat",
Self::FortyFiveDown => "FortyFiveDown",
Self::SingleDown => "SingleDown",
Self::DoubleDown => "DoubleDown",
Self::TripleDown => "TripleDown",
Self::NotComputable => "NOT COMPUTABLE",
Self::RateOutOfRange => "RATE OUT OF RANGE",
Self::Unknown(s) => s,
}
}
#[must_use]
pub const fn arrow(&self) -> &'static str {
match self {
Self::TripleUp => "⤊",
Self::DoubleUp => "⇈",
Self::SingleUp => "↑",
Self::FortyFiveUp => "↗",
Self::Flat => "→",
Self::FortyFiveDown => "↘",
Self::SingleDown => "↓",
Self::DoubleDown => "⇊",
Self::TripleDown => "⤋",
Self::None => "⇼",
Self::NotComputable | Self::Unknown(_) => "-",
Self::RateOutOfRange => "⇕",
}
}
#[must_use]
pub const fn trend_code(&self) -> u8 {
match self {
Self::None => 0,
Self::DoubleUp => 1,
Self::SingleUp => 2,
Self::FortyFiveUp => 3,
Self::Flat => 4,
Self::FortyFiveDown => 5,
Self::SingleDown => 6,
Self::DoubleDown => 7,
Self::TripleUp | Self::TripleDown | Self::NotComputable | Self::Unknown(_) => 8,
Self::RateOutOfRange => 9,
}
}
}
impl fmt::Display for Direction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl Serialize for Direction {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
s.serialize_str(self.as_str())
}
}
impl<'de> Deserialize<'de> for Direction {
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
String::deserialize(d).map(|s| Self::parse(&s))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn round_trips_every_value() {
for s in [
"NONE",
"TripleUp",
"DoubleUp",
"SingleUp",
"FortyFiveUp",
"Flat",
"FortyFiveDown",
"SingleDown",
"DoubleDown",
"TripleDown",
"NOT COMPUTABLE",
"RATE OUT OF RANGE",
"SomethingNew",
] {
assert_eq!(Direction::parse(s).as_str(), s);
}
assert_eq!(
Direction::parse("SomethingNew"),
Direction::Unknown("SomethingNew".into())
);
}
}