mod value;
pub use self::value::{Value, ValueKind};
use super::TrackID;
use std::collections::HashMap;
use std::time::Duration;
#[derive(Debug, Default, Clone)]
pub struct Metadata {
values: HashMap<String, Value>,
}
impl Metadata {
pub fn new<S>(track_id: S) -> Self
where
S: Into<String>,
{
let mut values = HashMap::with_capacity(1);
values.insert(
String::from("mpris:trackid"),
Value::String(track_id.into()),
);
Metadata { values }
}
pub fn get(&self, key: &str) -> Option<&Value> {
self.values.get(key)
}
pub fn track_id(&self) -> Option<TrackID> {
self.get("mpris:trackid")
.and_then(Value::as_str)
.and_then(|v| TrackID::new(v).ok())
}
pub fn album_artists(&self) -> Option<Vec<&str>> {
self.get("xesam:albumArtist").and_then(Value::as_str_array)
}
pub fn album_name(&self) -> Option<&str> {
self.get("xesam:album").and_then(Value::as_str)
}
pub fn art_url(&self) -> Option<&str> {
self.get("mpris:artUrl").and_then(Value::as_str)
}
pub fn artists(&self) -> Option<Vec<&str>> {
self.get("xesam:artist").and_then(Value::as_str_array)
}
pub fn auto_rating(&self) -> Option<f64> {
self.get("xesam:autoRating").and_then(Value::as_f64)
}
pub fn disc_number(&self) -> Option<i32> {
self.get("xesam:discNumber").and_then(Value::as_i32)
}
pub fn length_in_microseconds(&self) -> Option<u64> {
match self.get("mpris:length") {
Some(Value::I64(len)) => Some(*len as u64),
Some(Value::U64(len)) => Some(*len),
Some(_) => None,
None => None,
}
}
pub fn length(&self) -> Option<Duration> {
use crate::extensions::DurationExtensions;
self.length_in_microseconds().map(Duration::from_micros_ext)
}
pub fn title(&self) -> Option<&str> {
self.get("xesam:title").and_then(Value::as_str)
}
pub fn track_number(&self) -> Option<i32> {
self.get("xesam:trackNumber").and_then(Value::as_i32)
}
pub fn url(&self) -> Option<&str> {
self.get("xesam:url").and_then(Value::as_str)
}
pub fn as_hashmap(&self) -> HashMap<&str, &Value> {
self.iter().collect()
}
pub fn iter(&self) -> impl Iterator<Item = (&str, &Value)> {
self.values.iter().map(|(k, v)| (k.as_str(), v))
}
pub fn keys(&self) -> impl Iterator<Item = &str> {
self.values.keys().map(String::as_str)
}
pub fn is_empty(&self) -> bool {
self.values.is_empty()
}
}
impl IntoIterator for Metadata {
type Item = (String, Value);
type IntoIter = std::collections::hash_map::IntoIter<String, Value>;
fn into_iter(self) -> Self::IntoIter {
self.values.into_iter()
}
}
#[allow(clippy::implicit_hasher)]
impl From<Metadata> for HashMap<String, Value> {
fn from(metadata: Metadata) -> Self {
metadata.values
}
}
impl From<HashMap<String, Value>> for Metadata {
fn from(values: HashMap<String, Value>) -> Self {
Metadata { values }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_creates_new_metadata() {
let metadata = Metadata::new("/foo");
assert_eq!(metadata.track_id(), Some(TrackID::new("/foo").unwrap()));
}
#[test]
fn it_supports_blank_metadata() {
let metadata = Metadata::from(HashMap::new());
assert_eq!(metadata.track_id(), None);
}
#[test]
fn it_builds_values_hash() {
let mut input_hash: HashMap<String, Value> = HashMap::new();
input_hash.insert(String::from("xesam:trackNumber"), Value::from(42));
let metadata = Metadata::from(input_hash.clone());
let output_hash = metadata.as_hashmap();
assert_eq!(input_hash.get("xesam:trackNumber"), Some(&Value::I32(42)));
assert_eq!(output_hash.get("xesam:trackNumber"), Some(&&Value::I32(42)));
}
#[test]
fn it_has_iterators() {
let mut input_hash: HashMap<String, Value> = HashMap::new();
input_hash.insert(String::from("xesam:trackNumber"), Value::from(42));
let metadata = Metadata::from(input_hash);
let keys: Vec<&str> = metadata.keys().collect();
assert_eq!(keys, vec!["xesam:trackNumber"]);
let keyvals: Vec<(&str, &Value)> = metadata.iter().collect();
assert_eq!(keyvals, vec![("xesam:trackNumber", &Value::I32(42))]);
for (key, val) in metadata {
assert_eq!(key, String::from("xesam:trackNumber"));
assert_eq!(val, Value::I32(42));
}
}
#[test]
fn from_hashmap_artist_string() {
use std::iter::FromIterator;
let metadata = Metadata::from(HashMap::from_iter(
vec![(String::from("xesam:artist"), Value::from("Agnes Obel"))].into_iter(),
));
assert_eq!(metadata.artists(), Some(vec!["Agnes Obel"]));
}
#[test]
fn from_hashmap_artists_list() {
use std::iter::FromIterator;
let metadata = Metadata::from(HashMap::from_iter(
vec![(
String::from("xesam:artist"),
Value::from(vec![Value::from("Agnes Obel")]),
)]
.into_iter(),
));
assert_eq!(metadata.artists(), Some(vec!["Agnes Obel"]));
}
}