convert to library

This commit is contained in:
kageru 2020-06-20 22:50:33 +02:00
parent 000eb9945a
commit 8253e4b5de
Signed by: kageru
GPG Key ID: 8282A2BEA4ADA3D2
6 changed files with 194 additions and 96 deletions

73
Cargo.lock generated Normal file
View File

@ -0,0 +1,73 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "envy"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f938a4abd5b75fe3737902dbc2e79ca142cc1526827a9e40b829a086758531a9"
dependencies = [
"serde",
]
[[package]]
name = "mparsed"
version = "0.1.0"
dependencies = [
"envy",
"serde",
]
[[package]]
name = "proc-macro2"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
dependencies = [
"proc-macro2",
]
[[package]]
name = "serde"
version = "1.0.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9124df5b40cbd380080b2cc6ab894c040a3070d995f5c9dc77e18c34a8ae37d"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f2c3ac8e6ca1e9c80b8be1023940162bf81ae3cffbb1809474152f2ce1eb250"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "syn"
version = "1.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5304cfdf27365b7585c25d4af91b35016ed21ef88f17ced89c7093b43dba8b6"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "unicode-xid"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"

View File

@ -4,9 +4,9 @@ version = "0.1.0"
authors = ["kageru <kageru@encode.moe>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
serde = { version = "1.0", features = ["derive"] }
#itertools = "0.9"
envy = "0.4"
[lib]

8
rustfmt.toml Normal file
View File

@ -0,0 +1,8 @@
newline_style = "Unix"
max_width = 140
tab_spaces = 2
imports_layout = "Horizontal"
merge_imports = true
struct_field_align_threshold = 25
where_single_line = true
edition = "2018"

102
src/lib.rs Normal file
View File

@ -0,0 +1,102 @@
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()
}
);
}
}

View File

@ -1,87 +0,0 @@
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<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
}
}
fn deserialize_response<'a, I: Iterator<Item = &'a str>>(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)
}
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);
}

View File

@ -2,7 +2,7 @@ use helpers::*;
use serde::Deserialize;
use std::time::Duration;
#[derive(Deserialize, Clone, Debug)]
#[derive(Deserialize, Clone, Debug, Default, PartialEq)]
pub struct Track {
pub file: String,
pub artist_sort: Option<String>,
@ -18,18 +18,20 @@ pub struct Track {
pub track: Option<u32>,
pub album: Option<String>,
pub artist: Option<String>,
pub pos: Option<u32>,
pub id: Option<u32>,
pub pos: u32,
pub id: u32,
// TODO: use proper time here
#[serde(rename = "last-modified")]
pub last_modified: Option<String>,
pub original_date: Option<String>,
pub time: Option<String>,
pub original_date: Option<u16>,
// probably not needed. it’s just the duration but as an int
// pub time: u16,
pub format: Option<String>,
#[serde(deserialize_with = "de_time_float")]
pub duration: Duration,
pub label: Option<String>,
pub date: Option<String>,
pub disc: Option<u32>,
pub date: Option<u16>,
pub disc: Option<u16>,
pub musicbraiz_trackid: Option<String>,
pub musicbrainz_albumid: Option<String>,
pub musicbrainz_albumartistid: Option<String>,