use crate::meme::{Matcher, Meme, MemeIdent}; use serde::{ de::{self, Deserializer, MapAccess, Visitor}, Deserialize, }; use std::path::PathBuf; use url::Url; #[derive(Debug, Deserialize)] pub struct Config { pub homeserver_url: Url, pub user_id: String, pub password: String, pub device_name: Option, pub sendmeme_command: Option, pub memes: Vec, pub store_path: PathBuf, #[serde(default = "default_clear_threshold")] pub clear_cache_threshold: u32, } fn default_clear_threshold() -> u32 { 10 } impl<'de> Deserialize<'de> for Meme { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { const FIELDS: &[&str] = &["keyword", "id", "randomcat"]; enum Field { Keyword, Ident(IdentField), Matcher, MatchCase, } enum IdentField { RandomCat, Id, } impl<'de> Deserialize<'de> for Field { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { struct Vis; impl<'de> Visitor<'de> for Vis { type Value = Field; fn expecting( &self, fmt: &mut std::fmt::Formatter<'_>, ) -> Result<(), std::fmt::Error> { fmt.write_str("a field for a meme") } fn visit_str(self, v: &str) -> Result where E: de::Error, { Ok(match v { "keyword" => Field::Keyword, "randomcat" => Field::Ident(IdentField::RandomCat), "id" => Field::Ident(IdentField::Id), "match" => Field::Matcher, "match_case" => Field::MatchCase, _ => return Err(de::Error::unknown_field(v, FIELDS)), }) } } deserializer.deserialize_identifier(Vis) } } struct Vis; impl<'de> Visitor<'de> for Vis { type Value = Meme; fn expecting(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { fmt.write_str("a meme") } fn visit_map(self, mut map: A) -> Result where A: MapAccess<'de>, { let mut keyword = None; let mut ident = None; let mut matcher = None; let mut match_case = None; while let Some(key) = map.next_key()? { match key { Field::Keyword => { if keyword.is_some() { return Err(de::Error::duplicate_field("keyword")); } keyword = Some(map.next_value()?); }, Field::Ident(i) => { if ident.is_some() { return Err(de::Error::duplicate_field( "ident, can only have one.", )); } match i { IdentField::Id => { ident = Some(MemeIdent::Id(map.next_value()?)); }, IdentField::RandomCat => { ident = Some(MemeIdent::RandomCat(map.next_value()?)); }, } }, Field::Matcher => { if matcher.is_some() { return Err(de::Error::duplicate_field("match")); } matcher = Some(map.next_value()?); }, Field::MatchCase => { if match_case.is_some() { return Err(de::Error::duplicate_field("match")); } match_case = Some(map.next_value()?); }, } } let keyword = keyword.ok_or_else(|| de::Error::missing_field("keyword"))?; let ident = ident.ok_or_else(|| de::Error::missing_field("ident"))?; let matcher = matcher.unwrap_or(Matcher::Begins); let match_case = match_case.unwrap_or(false); Ok(Meme { keyword, ident, matcher, match_case, }) } } deserializer.deserialize_struct("Meme", FIELDS, Vis) } }