From 000eb9945a779f88269dd87c4f09795120d0a795 Mon Sep 17 00:00:00 2001 From: kageru Date: Sat, 20 Jun 2020 22:21:21 +0200 Subject: [PATCH] separate structs from main logic --- src/main.rs | 112 ++++--------------------------------------------- src/structs.rs | 89 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 104 deletions(-) create mode 100644 src/structs.rs diff --git a/src/main.rs b/src/main.rs index 0ff3fd2..55b09d5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,92 +1,19 @@ use serde::de; use serde::de::value::MapDeserializer; use serde::forward_to_deserialize_any; -use serde::Deserialize; use std::collections::HashMap; -use std::fmt; -use std::time::Duration; mod error; -use error::{MpdResult,Error}; +use error::{Error, MpdResult}; +mod structs; +pub use structs::Track; + +/// some unprintable character to separate repeated keys +const SEPARATOR: char = '\x02'; struct MPDeserializer<'de, Iter: Iterator> { inner: MapDeserializer<'de, Iter, Error>, } -/// Deserialize time from an integer that represents the seconds. -/// mpd uses int for the database stats (e.g. total time played). -fn de_time_int<'de, D>(deserializer: D) -> Result -where - D: de::Deserializer<'de>, -{ - u64::deserialize(deserializer).map(Duration::from_secs) -} - -/// Deserialize time from a float that represents the seconds. -/// mpd uses floats for the current status (e.g. time elapsed in song). -fn de_time_float<'de, D>(deserializer: D) -> Result, D::Error> -where - D: de::Deserializer<'de>, -{ - f64::deserialize(deserializer) - .map(Duration::from_secs_f64) - .map(Some) -} - -/// mpd uses bints (0 or 1) to represent booleans, -/// so we need a special parser for those. -fn de_bint<'de, D>(deserializer: D) -> Result -where - D: de::Deserializer<'de>, -{ - match u8::deserialize(deserializer)? { - 0 => Ok(false), - 1 => Ok(true), - n => Err(de::Error::invalid_value( - de::Unexpected::Unsigned(n as u64), - &"zero or one", - )), - } -} - -#[derive(Deserialize, Clone, Debug)] -pub struct Track { - pub file: String, - pub artist_sort: Option, - pub album_artist: Option, - pub album_sort: Option, - pub album_artist_sort: Option, - #[serde(deserialize_with = "string_or_vec")] - #[serde(default)] - #[serde(rename = "performer")] - pub performers: Vec, - pub genre: Option, - pub title: Option, - pub track: Option, - pub album: Option, - pub artist: Option, - pub pos: Option, - pub id: Option, - // TODO: use proper time here - pub last_modified: Option, - pub original_date: Option, - pub time: Option, - pub format: Option, - #[serde(deserialize_with = "de_time_float")] - pub duration: Option, - pub label: Option, - pub date: Option, - pub disc: Option, - pub musicbraiz_trackid: Option, - pub musicbrainz_albumid: Option, - pub musicbrainz_albumartistid: Option, - pub musicbrainz_artistid: Option, - pub musicbraiz_releasetrackid: Option, - pub composer: Option, -} - -// some unprintable character -const SEPARATOR: char = '\x02'; - impl<'de, Iter: Iterator> de::Deserializer<'de> for MPDeserializer<'de, Iter> { @@ -113,30 +40,6 @@ impl<'de, Iter: Iterator> de::Deserializer<'de> } } -fn string_or_vec<'de, D>(deserializer: D) -> Result, D::Error> -where - D: de::Deserializer<'de>, -{ - struct StringOrVec(); - - impl<'de> de::Visitor<'de> for StringOrVec { - type Value = Vec; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("string or list of strings") - } - - fn visit_str(self, value: &str) -> Result { - Ok(value - .split(SEPARATOR) - .map(std::string::String::from) - .collect()) - } - } - - deserializer.deserialize_any(StringOrVec()) -} - fn deserialize_response<'a, I: Iterator>(input: I) -> Result { let mut map: HashMap = HashMap::new(); for line in input { @@ -159,6 +62,7 @@ fn deserialize_response<'a, I: Iterator>(input: I) -> Result, + pub album_artist: Option, + pub album_sort: Option, + pub album_artist_sort: Option, + #[serde(deserialize_with = "string_or_vec")] + #[serde(default)] + #[serde(rename = "performer")] + pub performers: Vec, + pub genre: Option, + pub title: Option, + pub track: Option, + pub album: Option, + pub artist: Option, + pub pos: Option, + pub id: Option, + // TODO: use proper time here + pub last_modified: Option, + pub original_date: Option, + pub time: Option, + pub format: Option, + #[serde(deserialize_with = "de_time_float")] + pub duration: Duration, + pub label: Option, + pub date: Option, + pub disc: Option, + pub musicbraiz_trackid: Option, + pub musicbrainz_albumid: Option, + pub musicbrainz_albumartistid: Option, + pub musicbrainz_artistid: Option, + pub musicbraiz_releasetrackid: Option, + pub composer: Option, +} + +/// Deserialization helpers to handle the quirks of mpd’s output. +mod helpers { + use crate::SEPARATOR; + use serde::de; + use serde::Deserialize; + use std::time::Duration; + + /// Deserialize time from an integer that represents the seconds. + /// mpd uses int for the database stats (e.g. total time played). + pub fn de_time_int<'de, D>(deserializer: D) -> Result + where + D: de::Deserializer<'de>, + { + u64::deserialize(deserializer).map(Duration::from_secs) + } + + /// Deserialize time from a float that represents the seconds. + /// mpd uses floats for the current status (e.g. time elapsed in song). + pub fn de_time_float<'de, D>(deserializer: D) -> Result + where + D: de::Deserializer<'de>, + { + f64::deserialize(deserializer).map(Duration::from_secs_f64) + } + + pub fn string_or_vec<'de, D>(deserializer: D) -> Result, D::Error> + where + D: de::Deserializer<'de>, + { + String::deserialize(deserializer) + .map(|s| s.split(SEPARATOR).map(std::string::String::from).collect()) + } + + /// mpd uses bints (0 or 1) to represent booleans, + /// so we need a special parser for those. + pub fn de_bint<'de, D>(deserializer: D) -> Result + where + D: de::Deserializer<'de>, + { + match u8::deserialize(deserializer)? { + 0 => Ok(false), + 1 => Ok(true), + n => Err(de::Error::invalid_value( + de::Unexpected::Unsigned(n as u64), + &"zero or one", + )), + } + } +}