use reqwest::StatusCode; use super::deserialize as de; use std::sync::Arc; use std::collections::BTreeMap; pub mod model { use std::sync::Arc; use std::collections::BTreeMap; /// The theme representation of a user. #[derive(serde::Serialize, Debug)] pub struct User { /// Their username. pub name: Arc, /// Their "Display Name". pub realname: Arc, /// True if user subscribes to last.fm pro. pub pro_subscriber: bool, /// Total scrobbles. pub scrobble_count: u64, /// Number of artists in library. pub artist_count: u64, /// Number of tracks in library. pub track_count: u64, /// Number of albums in library. pub album_count: u64, /// Link to user's profile picture. pub image_url: Arc, /// Link to user's profile. pub url: Arc } /// The theme representation of an artist #[derive(serde::Serialize, Debug)] pub struct Artist { /// The artist's name. pub name: Arc, /// A link to their current image. pub image_url: Arc, /// A link to their last.fm page. pub url: Arc } /// The theme representation of a user's most recently scrobbled track. #[derive(serde::Serialize, Debug)] pub struct Scrobble { /// The name of the track. pub name: Arc, /// The name of its album. pub album: Arc, /// The artist who made it. pub artist: Artist, /// A link to the track image. pub image_url: Arc, /// True if the user is currently scrobbling it, false if it's just the most recently played track. pub now_playing: bool, /// A link to the track's last.fm page. pub url: Arc, /// True if the user has loved the track. pub loved: bool } /// The context passed in to all themes. /// /// Serialized as untagged, so themes should check if `error` is set and decide whether to show an error state or try rendering user info. #[derive(serde::Serialize, Debug)] #[serde(untagged)] pub enum Data { /// Contains text explaining a potential error. Error { error: &'static str }, /// Contains data about a user and what they're listening to. Data { user: User, scrobble: Scrobble, query: BTreeMap } } } #[derive(Debug)] pub struct ResponseCtx(pub model::Data, pub StatusCode); impl From<(Result, (StatusCode, &'static str)>, BTreeMap)> for ResponseCtx { fn from(v: (Result, (StatusCode, &'static str)>, BTreeMap)) -> ResponseCtx { let (v, q) = v; match v { Ok(a) => { let (user, track) = a.as_ref(); ResponseCtx(model::Data::Data { user: model::User { name: user.name.clone(), realname: user.realname.clone(), pro_subscriber: user.subscriber, scrobble_count: user.playcount, artist_count: user.artist_count, track_count: user.track_count, album_count: user.track_count, image_url: user.images.iter().max_by(|a, b| a.size.cmp(&b.size)).map(|a| a.url.clone()).unwrap_or_else(|| "".into()), url: user.url.clone() }, scrobble: model::Scrobble { name: track.name.clone(), album: track.album.name.clone(), artist: model::Artist { name: track.artist.name.clone(), image_url: track.artist.images.iter().max_by(|a, b| a.size.cmp(&b.size)).map(|a| a.url.clone()).unwrap_or_else(|| "".into()), url: track.artist.url.clone().unwrap_or_else(|| "".into()) }, image_url: track.images.iter().max_by(|a, b| a.size.cmp(&b.size)).map(|a| a.url.clone()).unwrap_or_else(|| "".into()), now_playing: track.attr.nowplaying, url: track.url.clone(), loved: track.loved.unwrap_or(false) }, query: q }, StatusCode::OK) }, Err((status, error)) => { ResponseCtx(model::Data::Error {error}, status) } } } }