use serde::de; use serde::de::value::MapDeserializer; use serde::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 } } fn deserialize_response<'a, I: Iterator>(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) } fn main() { let input_str = "file: Youtube Rip/VVVVVV Medley.flac Last-Modified: 2018-03-07T13:18:01Z Album: VVVVVV OST / PPPPPP Artist: FamilyJules7x Composer: Magnus Pålsson Date: 2014 Genre: Video Game Music Title: VVVVVV Medley Time: 433 duration: 432.727 Pos: 1548 Id: 1549"; let s = deserialize_response(input_str.lines()); println!("{:?}", s); }