use serde::{de, de::value::MapDeserializer, forward_to_deserialize_any}; use std::collections::HashMap; mod 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>, } impl<'de, Iter: Iterator> de::Deserializer<'de> for MPDeserializer<'de, Iter> { type Error = Error; fn deserialize_any(self, visitor: V) -> MpdResult where V: de::Visitor<'de> { self.deserialize_map(visitor) } fn deserialize_map(self, visitor: V) -> MpdResult where V: de::Visitor<'de> { visitor.visit_map(self.inner) } forward_to_deserialize_any! { bool char str string bytes byte_buf unit unit_struct seq tuple tuple_struct struct identifier ignored_any i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 option newtype_struct enum } } pub fn deserialize_response<'a, I: Iterator, T: de::Deserialize<'a>>(input: I) -> Result { let mut map: HashMap = HashMap::new(); for line in input { if line.starts_with("OK") { break; } else if let Some(message) = line.strip_prefix("ACK") { return Err(Error::from_str(message.trim())); } let mut fields = line.splitn(2, ": "); match (fields.next(), fields.next()) { (Some(k), Some(v)) => { if let Some(existing) = map.get_mut(k) { existing.push(SEPARATOR); existing.push_str(v); } else { map.insert(k.to_string(), v.to_string()); } } _ => panic!("invalid response line: {:?}", line), } } return Ok(envy::from_iter(map).unwrap()); // Eventually, I’d like to use my own deserializer instead of envy // let deser = MPDeserializer { // inner: MapDeserializer::new(map.into_iter()), // }; // Song::deserialize(deser) } #[cfg(test)] mod tests { use super::*; #[test] fn track_de_test() { let input_str = "file: American Pie.flac Last-Modified: 2019-12-16T21:38:54Z Album: American Pie Artist: Don McLean Date: 1979 Genre: Rock Title: American Pie Track: 1 Time: 512 duration: 512.380 Pos: 1367 Id: 1368 OK"; let t = deserialize_response::<'_, _, Track>(input_str.lines()).unwrap(); assert_eq!( t, Track { file: "American Pie.flac".to_string(), last_modified: Some("2019-12-16T21:38:54Z".to_string()), album: Some("American Pie".to_string()), artist: Some("Don McLean".to_string()), date: Some(1979), genre: Some("Rock".to_string()), title: Some("American Pie".to_string()), track: Some(1), duration: std::time::Duration::from_secs_f64(512.380), pos: 1367, id: 1368, ..Track::default() } ); } }