use std::fmt;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum Frequency {
Daily,
Weekly,
Biweekly,
Monthly,
Quarterly,
Semiannual,
Annual,
Other(String),
}
impl Frequency {
fn from_label(label: &str) -> Self {
match label {
"Daily" => Self::Daily,
"Weekly" => Self::Weekly,
"Biweekly" => Self::Biweekly,
"Monthly" => Self::Monthly,
"Quarterly" => Self::Quarterly,
"Semiannual" => Self::Semiannual,
"Annual" => Self::Annual,
other => Self::Other(other.to_owned()),
}
}
pub fn label(&self) -> &str {
match self {
Self::Daily => "Daily",
Self::Weekly => "Weekly",
Self::Biweekly => "Biweekly",
Self::Monthly => "Monthly",
Self::Quarterly => "Quarterly",
Self::Semiannual => "Semiannual",
Self::Annual => "Annual",
Self::Other(label) => label,
}
}
pub fn query_code(&self) -> &str {
match self {
Self::Daily => "d",
Self::Weekly => "w",
Self::Biweekly => "bw",
Self::Monthly => "m",
Self::Quarterly => "q",
Self::Semiannual => "sa",
Self::Annual => "a",
Self::Other(label) => label,
}
}
}
impl fmt::Display for Frequency {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.label())
}
}
impl<'de> Deserialize<'de> for Frequency {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let label = String::deserialize(deserializer)?;
Ok(Self::from_label(&label))
}
}
impl Serialize for Frequency {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.label())
}
}
#[cfg(feature = "schemars")]
impl schemars::JsonSchema for Frequency {
fn schema_name() -> std::borrow::Cow<'static, str> {
"Frequency".into()
}
fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
<String as schemars::JsonSchema>::json_schema(generator)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn known_label_maps_to_variant() {
assert_eq!(
serde_json::from_str::<Frequency>("\"Monthly\"").unwrap(),
Frequency::Monthly
);
}
#[test]
fn unknown_label_is_preserved_verbatim() {
assert_eq!(
serde_json::from_str::<Frequency>("\"Weekly, Ending Friday\"").unwrap(),
Frequency::Other("Weekly, Ending Friday".to_owned())
);
}
#[test]
fn display_round_trips_the_label() {
assert_eq!(Frequency::Annual.to_string(), "Annual");
assert_eq!(
Frequency::Other("Weekly, Ending Friday".to_owned()).to_string(),
"Weekly, Ending Friday"
);
}
#[test]
fn query_codes_match_fred() {
assert_eq!(Frequency::Monthly.query_code(), "m");
assert_eq!(Frequency::Semiannual.query_code(), "sa");
assert_eq!(Frequency::Daily.query_code(), "d");
}
#[test]
fn serializes_to_its_label_and_round_trips() {
assert_eq!(
serde_json::to_string(&Frequency::Monthly).unwrap(),
"\"Monthly\""
);
let other = Frequency::Other("Weekly, Ending Friday".to_owned());
let json = serde_json::to_string(&other).unwrap();
assert_eq!(json, "\"Weekly, Ending Friday\"");
assert_eq!(serde_json::from_str::<Frequency>(&json).unwrap(), other);
}
}