use std::fmt;
use std::str::FromStr;
use serde::{Deserialize, Serialize};
#[derive(
Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default,
)]
#[serde(rename_all = "lowercase")]
pub enum SyncMode {
#[default]
Full,
Metadata,
Off,
}
impl SyncMode {
const fn rank(self) -> u8 {
match self {
SyncMode::Full => 2,
SyncMode::Metadata => 1,
SyncMode::Off => 0,
}
}
pub fn most_restrictive(self, other: SyncMode) -> SyncMode {
if self.rank() <= other.rank() { self } else { other }
}
pub fn syncs_blob(self) -> bool {
matches!(self, SyncMode::Full)
}
pub fn syncs_state(self) -> bool {
matches!(self, SyncMode::Full | SyncMode::Metadata)
}
pub fn syncs_anything(self) -> bool {
!matches!(self, SyncMode::Off)
}
pub fn as_str(self) -> &'static str {
match self {
SyncMode::Full => "full",
SyncMode::Metadata => "metadata",
SyncMode::Off => "off",
}
}
pub fn from_token_or_default(s: &str) -> SyncMode {
s.parse().unwrap_or_default()
}
}
impl fmt::Display for SyncMode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl FromStr for SyncMode {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.trim().to_ascii_lowercase().as_str() {
"full" => Ok(SyncMode::Full),
"metadata" | "meta" => Ok(SyncMode::Metadata),
"off" | "disabled" | "none" => Ok(SyncMode::Off),
_ => Err(()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_is_full() {
assert_eq!(SyncMode::default(), SyncMode::Full);
}
#[test]
fn most_restrictive_takes_the_lower_rank() {
use SyncMode::*;
assert_eq!(Full.most_restrictive(Metadata), Metadata);
assert_eq!(Metadata.most_restrictive(Full), Metadata);
assert_eq!(Full.most_restrictive(Off), Off);
assert_eq!(Off.most_restrictive(Metadata), Off);
assert_eq!(Full.most_restrictive(Full), Full);
assert_eq!(Metadata.most_restrictive(Off), Off.most_restrictive(Metadata));
}
#[test]
fn capability_predicates() {
assert!(SyncMode::Full.syncs_blob());
assert!(!SyncMode::Metadata.syncs_blob());
assert!(!SyncMode::Off.syncs_blob());
assert!(SyncMode::Full.syncs_state());
assert!(SyncMode::Metadata.syncs_state());
assert!(!SyncMode::Off.syncs_state());
assert!(SyncMode::Full.syncs_anything());
assert!(SyncMode::Metadata.syncs_anything());
assert!(!SyncMode::Off.syncs_anything());
}
#[test]
fn parse_round_trips_and_accepts_aliases() {
for m in [SyncMode::Full, SyncMode::Metadata, SyncMode::Off] {
assert_eq!(m.as_str().parse::<SyncMode>(), Ok(m));
}
assert_eq!("meta".parse(), Ok(SyncMode::Metadata));
assert_eq!(" OFF ".parse(), Ok(SyncMode::Off));
assert_eq!("disabled".parse(), Ok(SyncMode::Off));
assert_eq!(SyncMode::from_token_or_default("nonsense"), SyncMode::Full);
}
#[test]
fn serde_uses_lowercase_tokens() {
let json = serde_json::to_string(&SyncMode::Metadata).unwrap();
assert_eq!(json, "\"metadata\"");
let back: SyncMode = serde_json::from_str("\"off\"").unwrap();
assert_eq!(back, SyncMode::Off);
}
}