// SPDX-License-Identifier: AGPL-3.0-only use std::collections::HashMap; use std::sync::Arc; use serde::{Deserialize, Deserializer, de}; use serde_json::Value; fn str_num<'de, D, T>(d: D) -> Result where D: Deserializer<'de>, T: From { struct Visitor; impl<'v> de::Visitor<'v> for Visitor { type Value = u64; fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "a value which can be interpreted as a uint") } fn visit_str(self, v: &str) -> Result where E: de::Error { v.parse().map_err(|_| de::Error::invalid_value(de::Unexpected::Str(v), &"a string which can be parsed as a uint")) } fn visit_u64(self, v: u64) -> Result where E: de::Error { Ok(v) } } d.deserialize_any(Visitor).map(Into::into) } fn str_bool<'de, D, T>(d: D) -> Result where D: Deserializer<'de>, T: From{ struct Visitor; impl<'v> de::Visitor<'v> for Visitor { type Value = bool; fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "a value which can be interpreted as a uint") } fn visit_str(self, v: &str) -> Result where E: de::Error { match v.to_ascii_lowercase().as_str() { "true" | "1" => Ok(true), "false" | "0" => Ok(false), _ => Err(de::Error::invalid_value(de::Unexpected::Str(v), &"a string which can be parsed as a bool")) } } fn visit_bool(self, v: bool) -> Result where E: de::Error { Ok(v) } } d.deserialize_any(Visitor).map(Into::into) } #[derive(Deserialize, Debug)] pub struct TimeStamp { #[serde(alias = "unixtime")] #[serde(alias = "uts")] #[serde(deserialize_with = "str_num")] pub unix_timestamp: u64, #[serde(rename = "#text")] pub text: Value } #[derive(Deserialize, Debug)] pub struct Artist { #[serde(alias = "#text")] pub name: Arc, #[serde(default)] pub url: Option> } #[derive(Deserialize, Debug, Ord, PartialOrd, Eq, PartialEq)] #[serde(rename_all = "lowercase")] #[repr(u8)] pub enum ImageSize { Small, Medium, Large, ExtraLarge } #[derive(Deserialize, Debug)] pub struct Image { pub size: ImageSize, #[serde(alias = "#text")] pub url: Arc, } #[derive(Deserialize, Debug)] pub struct Album { pub mbid: Option>, #[serde(alias = "#text")] pub title: Arc, #[serde(default)] #[serde(rename = "image")] pub images: Vec, } #[derive(Deserialize, Debug)] pub struct Track { pub artist: Artist, #[serde(alias = "image")] #[serde(default)] pub images: Vec, pub mbid: Option>, pub album: Option, pub name: Arc, pub url: Arc, #[serde(deserialize_with = "str_num")] pub duration: u64, #[serde(deserialize_with = "str_num")] pub listeners: u64, #[serde(deserialize_with = "str_num")] pub playcount: u64, #[serde(default)] #[serde(rename = "userloved")] #[serde(deserialize_with = "str_bool")] pub loved: Option, #[serde(deserialize_with = "str_num")] pub userplaycount: u64, } #[derive(Deserialize, Debug)] pub struct GetTrackInfo { pub track: Track } #[derive(Default, Deserialize, Debug)] pub struct TrackAttr { #[serde(default)] #[serde(deserialize_with = "str_bool")] pub nowplaying: bool, #[serde(flatten)] pub rest: HashMap, Value>, } #[derive(Deserialize, Debug)] pub struct ArtistStub { #[serde(alias = "#text")] pub name: Arc } #[derive(Deserialize, Debug)] pub struct TrackStub { pub name: Arc, pub artist: ArtistStub, #[serde(default)] pub date: Option, #[serde(rename = "@attr")] #[serde(default)] pub attr: TrackAttr, } #[derive(Deserialize, Debug)] pub struct RecentTracks { pub track: Vec } #[derive(Deserialize, Debug)] pub struct GetRecentTracks { pub recenttracks: RecentTracks } #[derive(Deserialize, Debug)] pub struct User { pub name: Arc, pub realname: Arc, #[serde(deserialize_with = "str_num")] pub playcount: u64, #[serde(deserialize_with = "str_num")] pub artist_count: u64, #[serde(deserialize_with = "str_num")] pub playlists: u64, #[serde(deserialize_with = "str_num")] pub track_count: u64, #[serde(deserialize_with = "str_num")] pub album_count: u64, #[serde(rename = "image")] pub images: Vec, pub registered: TimeStamp, pub url: Arc } #[derive(Deserialize, Debug)] pub struct GetUserInfo { pub user: User }