use std::str::FromStr; use serde::{Deserialize, Deserializer, Serialize}; use serde_yaml::Value; #[derive(Serialize, Deserialize)] pub struct Challenge { pub japanese: Vec>>, pub english: Option>, pub song: Option, pub youtube: Option, pub spotify: Option, pub translation: Option, pub suggester: Option, pub date: chrono::NaiveDate, } #[derive(Serialize, Deserialize)] pub struct Song { pub japanese: Option, pub english: Option, } #[derive(Serialize)] pub struct ChallengeWord { pub dictionary: Option, pub pos: Option, pub text: Option>, } impl<'de> Deserialize<'de> for ChallengeWord { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { let value = Deserialize::deserialize(deserializer)?; let map = if let Value::Mapping(map) = value { map } else { return Err(serde::de::Error::invalid_type( serde::de::Unexpected::Other("not a string or map"), &"a string or map", )); }; let dictionary = map.get("dictionary").and_then(|value| match value { Value::Null => None, _ => Some(value.as_str().unwrap().to_owned()), }); let pos: Option = map .get("pos") .map(|value| value.as_str().unwrap().parse().unwrap()); Ok(ChallengeWord { dictionary, pos, text: map.get("text").map(|value| match value { Value::String(string) => vec![Furigana { kanji: string.clone(), furigana: None, }], Value::Sequence(sequence) => sequence .iter() .map(|value| match value { Value::String(kanji) => Furigana { kanji: kanji.to_owned(), furigana: None, }, Value::Mapping(mapping) => { let (kanji, furigana) = mapping.iter().next().unwrap(); Furigana { kanji: kanji.as_str().unwrap().to_owned(), furigana: Some(furigana.as_str().unwrap().to_owned()), } } _ => panic!(), }) .collect(), _ => panic!(), }), }) } } #[derive(Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub enum PartOfSpeech { Noun, Adjective, Verb, Adverb, Particle, Phrase, } #[derive(Debug, PartialEq, Eq)] pub struct ParsePartOfSpeechError; impl FromStr for PartOfSpeech { type Err = ParsePartOfSpeechError; fn from_str(s: &str) -> Result { use PartOfSpeech::*; Ok(match s { "noun" => Noun, "adjective" => Adjective, "verb" => Verb, "adverb" => Adverb, "particle" => Particle, "phrase" => Phrase, _ => return Err(ParsePartOfSpeechError), }) } } #[derive(Serialize, Deserialize)] pub struct Furigana { pub kanji: String, pub furigana: Option, } #[derive(Serialize, Deserialize)] pub struct Translation { pub author: Option, pub site: TranslationSite, } #[derive(Serialize, Deserialize)] pub struct TranslationSite { pub name: String, pub link: String, }