Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
kageru | 46dc0ac622 | ||
kageru | c1499ff9b3 | ||
kageru | f3738b1af7 | ||
kageru | 5889512732 | ||
kageru | c3549cd230 |
|
@ -50,7 +50,7 @@ checksum = "bd7d4bd64732af4bf3a67f367c27df8520ad7e230c5817b8ff485864d80242b9"
|
|||
|
||||
[[package]]
|
||||
name = "mparsed"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"envy",
|
||||
|
|
11
README.md
11
README.md
|
@ -2,6 +2,8 @@
|
|||
|
||||
A serde parser for MPD responses. Includes mpd structs.
|
||||
|
||||
This project is on [crates.io](https://crates.io/crates/mparsed).
|
||||
|
||||
## Why?
|
||||
Because there are lots of mpd client libraries for Rust,
|
||||
but most (maybe all?) of them write the same awful deserialization code
|
||||
|
@ -18,6 +20,15 @@ match key {
|
|||
|
||||
And I figured just having a small crate that has all the types and does the serde magic for you would be nice for other people as well.
|
||||
|
||||
Now you can simply:
|
||||
|
||||
```rs
|
||||
let raw_response: Vec<String> = my_mpd_client.currentsong(); // <- this one is from your own code
|
||||
let track: Track = mparsed::deserialize_response(raw_response.iter());
|
||||
```
|
||||
|
||||
No more hand-written deserialization logic.
|
||||
|
||||
Oh, and it’s a good learning opportunity for me.
|
||||
Serde seemed like a library I should learn more about.
|
||||
|
||||
|
|
10
src/lib.rs
10
src/lib.rs
|
@ -58,6 +58,7 @@ pub fn parse_response<'a, I: Iterator<Item = &'a str>, T: de::DeserializeOwned>(
|
|||
}
|
||||
|
||||
/// Parse an iterator of string slices into a vector of `T`, splitting at any occurence of `first_key`.
|
||||
///
|
||||
/// One possible use for this is the `playlistinfo` command which returns all items in the current
|
||||
/// playlist, where the `file: ` key denotes the start of a new item.
|
||||
///
|
||||
|
@ -106,9 +107,14 @@ pub fn parse_response_vec<'a, I: Iterator<Item = &'a str>, T: de::DeserializeOwn
|
|||
.collect()
|
||||
}
|
||||
|
||||
/// Parse the `playlist` command, a list of key-value pairs, as a vector of filenames.
|
||||
/// Parse the `playlist` command as a vector of filenames.
|
||||
///
|
||||
/// The playlist index of each item is *not* included because, if needed,
|
||||
/// it can easily be added with `.enumerate()`.
|
||||
///
|
||||
/// Note: The MPD protocol documentation suggests using `playlistinfo` instead,
|
||||
/// which returns a superset of this commands output,
|
||||
/// but this isn’t deprecated, so if you only need the filenames, it should be all you need.
|
||||
pub fn parse_playlist<'a, I: Iterator<Item = &'a str>>(input: I) -> MpdResult<Vec<&'a str>> {
|
||||
input
|
||||
// `iter.scan((), |(), item| predicate(item))` is equivalent to map_while(predicate),
|
||||
|
@ -433,7 +439,7 @@ OK";
|
|||
File {
|
||||
name: "Scans".into(),
|
||||
last_modified: DateTime::parse_from_rfc3339("2015-01-30T14:53:03Z").unwrap(),
|
||||
size: 0
|
||||
size: -1
|
||||
},
|
||||
File {
|
||||
name: "15 風ハ旅スル(スマートフォンゲーム「風パズル 黒猫と白猫の夢見た世界」テーマ曲).flac".into(),
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use chrono::prelude::*;
|
||||
use helpers::*;
|
||||
use serde::Deserialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{fmt, time::Duration};
|
||||
|
||||
/// All information about a track. This is returned by the `currentsong` or `queue` commands.
|
||||
#[derive(Deserialize, Clone, Debug, Default, PartialEq)]
|
||||
/// All information about a track. This is returned by the `currentsong`, `queue`, or
|
||||
/// `playlistinfo` commands.
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
|
||||
#[serde(default)]
|
||||
pub struct Track {
|
||||
pub file: String,
|
||||
|
@ -49,14 +50,15 @@ pub struct Track {
|
|||
|
||||
/// An empty struct that can be used as the response data for commands that only ever return `OK`
|
||||
/// or an error message, e.g. `consume` or other boolean toggles.
|
||||
#[derive(Deserialize, PartialEq, Debug)]
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||
pub struct UnitResponse {}
|
||||
|
||||
/// The position of an item in a list with an optional total length.
|
||||
///
|
||||
/// Can be used for e.g. track number,
|
||||
/// where `Position { 3, 12 }` represents track 3 of a CD with 12 tracks,
|
||||
/// or disc numbers in a multi-disc set.
|
||||
#[derive(Deserialize, Clone, Debug, Default, PartialEq)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
|
||||
pub struct Position {
|
||||
pub item_position: u16,
|
||||
pub total_items: Option<u16>,
|
||||
|
@ -78,7 +80,7 @@ impl fmt::Display for Position {
|
|||
/// i.e. on a server with no pulse or alsa output configured.
|
||||
/// Output via httpd might be used instead
|
||||
/// but will result in empty `volume` and `audio` fields.
|
||||
#[derive(Deserialize, Clone, Debug, Default, PartialEq)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
|
||||
#[serde(default)]
|
||||
pub struct Status {
|
||||
pub volume: Option<u8>,
|
||||
|
@ -117,23 +119,38 @@ pub struct Status {
|
|||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone, Debug, PartialEq)]
|
||||
/// An object in the file system, as returned by the `listfiles` command.
|
||||
///
|
||||
/// For directories, the `size` will be `-1`, and [`is_directory`] is provided to check that.
|
||||
///
|
||||
/// [`is_directory`]: #method.is_directory
|
||||
///
|
||||
/// Properly parsing `listfiles` into a Vector of some enum with variants for file and directory
|
||||
/// would make the deserialization code a lot more complicated, so I’m not interested in doing that
|
||||
/// at this point in time.
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||
pub struct File {
|
||||
#[serde(alias = "directory", rename = "file")]
|
||||
pub name: String,
|
||||
#[serde(rename = "last-modified")]
|
||||
pub last_modified: DateTime<FixedOffset>,
|
||||
#[serde(default)]
|
||||
pub size: usize,
|
||||
#[serde(default = "minus_one")]
|
||||
pub size: i64,
|
||||
}
|
||||
|
||||
fn minus_one() -> i64 {
|
||||
-1
|
||||
}
|
||||
|
||||
impl File {
|
||||
/// Returns true if this file is a directory.
|
||||
/// Internally, this just checks if `size == -1`.
|
||||
pub fn is_directory(&self) -> bool {
|
||||
self.size == 0
|
||||
self.size == -1
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone, Debug, PartialEq)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||
pub enum State {
|
||||
Stop,
|
||||
Play,
|
||||
|
@ -148,7 +165,7 @@ impl Default for State {
|
|||
}
|
||||
|
||||
/// Database statistics as returned by the `stats` command.
|
||||
#[derive(Deserialize, Clone, Debug, Default, PartialEq)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
|
||||
#[serde(default)]
|
||||
pub struct Stats {
|
||||
pub artists: u32,
|
||||
|
|
Loading…
Reference in New Issue
Block a user