diff --git a/.gitignore b/.gitignore index 62bd1a4..eccd7b4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,2 @@ -# ---> Rust -# Generated by Cargo -# will have compiled files and executables /target/ - -# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries -# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html -Cargo.lock - -# These are backup files generated by rustfmt **/*.rs.bk - diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..717b996 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "mparsed" +version = "0.1.0" +authors = ["kageru "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +pest = "2.1" +pest_derive = "2.1" +serde = { version = "1.0", features = ["derive"] } diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..8b14429 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,40 @@ +use pest; +use serde::de; +use std::fmt::{self, Display}; + +use crate::Rule; + +pub type MpdResult = std::result::Result; + +#[derive(Clone, Debug, PartialEq)] +pub struct Error { + pub message: String, +} + +impl From> for Error { + fn from(err: pest::error::Error) -> Self { + Error { + message: err.to_string(), + } + } +} + +impl de::Error for Error { + fn custom(msg: T) -> Self { + Error { + message: msg.to_string(), + } + } +} + +impl Display for Error { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str(&self.message) + } +} + +impl std::error::Error for Error { + fn description(&self) -> &str { + &self.message + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..d5dd324 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,105 @@ +#[macro_use] +extern crate pest_derive; +use pest::iterators::Pair; +use pest::Parser; +use serde::de; +use serde::de::value::MapDeserializer; +use serde::de::DeserializeOwned; +use serde::forward_to_deserialize_any; +use serde::Deserialize; +mod error; +use error::MpdResult; + +#[derive(Parser)] +#[grammar = "response.pest"] +struct MpdParser; + +struct MPDeserializer<'de, Iter: Iterator> { + inner: MapDeserializer<'de, Iter, error::Error>, +} + +impl<'de, Iter: Iterator> de::Deserializer<'de> + for MPDeserializer<'de, Iter> +{ + type Error = 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 + } +} + +#[derive(Debug, Deserialize)] +struct Song { + Title: String, + Artist: String, +} + +const input_var: &str = "Title: A song 星 +Artist: A name +OK mpd 0.21.23"; + +fn main() { + /* + let parsed = MpdParser::parse(Rule::response, input) + .expect("invalid response") + .next() + .unwrap(); + let last = parsed.into_inner().last().unwrap(); + match last.as_rule() { + Rule::ok => println!("response was ok"), + Rule::err => println!("response was an error"), + _ => unreachable!("Last response line should always be a status"), + } + let mut deser = MPDeserializer::from_str( + "Title: A song 星 + Artist: A name + OK mpd 0.21.23", + ) + .expect("broken deser"); + let d = Song::deserialize(&mut deser); + println!("{:?}", d); + */ + let mut map = std::collections::HashMap::new(); + let parser = MpdParser::parse(Rule::response, input_var) + .unwrap() + .next() + .unwrap(); + for pair in parser.into_inner() { + let rule = pair.as_rule(); + match rule { + Rule::kv => { + let mut fields = pair.into_inner(); + map.insert( + fields.next().unwrap().as_str().to_string(), + fields.next().unwrap().as_str().to_string(), + ); + } + Rule::ok => (), + Rule::err => panic!("received error response"), + _ => unreachable!(), + } + } + // ::<'_, _, Song> + dbg!(&map); + let deser = MPDeserializer { + inner: serde::de::value::MapDeserializer::new(map.into_iter()), + }; + let s = Song::deserialize(deser); + println!("{:?}", s); +} diff --git a/src/response.pest b/src/response.pest new file mode 100644 index 0000000..9faad89 --- /dev/null +++ b/src/response.pest @@ -0,0 +1,6 @@ +key = { ('a'..'z' | 'A'..'Z')+ } +val = { (!"\n" ~ ANY)* } +kv = { key ~ ": " ~ val ~ "\n" } +ok = { "OK" ~ " "? ~ val? } +err = {"ACK" ~ " "? ~ val? } +response = { kv* ~ (ok | err) }