mparsed/src/lib.rs
2020-06-20 22:50:33 +02:00

103 lines
2.9 KiB
Rust

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<Item = (&'de str, &'de str)>> {
inner: MapDeserializer<'de, Iter, Error>,
}
impl<'de, Iter: Iterator<Item = (&'de str, &'de str)>> de::Deserializer<'de> for MPDeserializer<'de, Iter> {
type Error = Error;
fn deserialize_any<V>(self, visitor: V) -> MpdResult<V::Value>
where V: de::Visitor<'de> {
self.deserialize_map(visitor)
}
fn deserialize_map<V>(self, visitor: V) -> MpdResult<V::Value>
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<Item = &'a str>, T: de::Deserialize<'a>>(input: I) -> Result<Track, Error> {
let mut map: HashMap<String, String> = 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()
}
);
}
}