aboutsummaryrefslogtreecommitdiffstats
path: root/src/ctx.rs
blob: 904667df2c20207d0e690e912f46c6d711f0fd2f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
use reqwest::StatusCode;
use super::deserialize as de;
use std::sync::Arc;
use std::collections::BTreeMap;
use super::font::FontQuery;

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<str>,
        /// Their "Display Name".
        pub realname: Arc<str>,
        
        /// 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<str>,
        
        /// Link to user's profile.
        pub url: Arc<str>
    }
    
    /// The theme representation of an artist
    #[derive(serde::Serialize, Debug)]
    pub struct Artist {
        /// The artist's name.
        pub name: Arc<str>,

        /// A link to their current image.
        pub image_url: Arc<str>,
        /// A link to their last.fm page.
        pub url: Arc<str>
    }

    /// 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<str>,
        /// The name of its album.
        pub album: Arc<str>,
        /// The artist who made it.
        pub artist: Artist,

        /// A link to the track image.
        pub image_url: Arc<str>,
        /// 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<str>,

        /// True if the user has loved the track.
        pub loved: bool
    }

    #[derive(serde::Serialize, Debug)]
    #[serde(untagged)]
    pub enum Font {
        Url { css: Arc<str> },
        Name { name: Arc<str> },
    }

    /// 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, font: Option<Font>, query: BTreeMap<String, String>,  }
    }
}
#[derive(Debug)]
pub struct ResponseCtx(pub model::Data, pub StatusCode);

impl From<(Result<Arc<(de::User, de::Track)>, (StatusCode, &'static str)>, Option<FontQuery>, BTreeMap<String, String>)> for ResponseCtx {
    fn from(v: (Result<Arc<(de::User, de::Track)>, (StatusCode, &'static str)>, Option<FontQuery>, BTreeMap<String, String>)) -> ResponseCtx {
        let (v, f, 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)
                    },
                    font: match f {
                        Some(FontQuery { font: Some(s), include_font: None }) => Some(model::Font::Name { name: s }),
                        Some(FontQuery { include_font: Some(s), .. }) => Some(model::Font::Url { css: format!("@font-face {{ font-family: 'included_font'; src: url('{}'); }}", s.replace("\\", "\\\\").replace("'", "\\'")).into() }),
                        _ => None,
                    },
                    query: q
                }, StatusCode::OK)
            },
            Err((status, error)) => {
                ResponseCtx(model::Data::Error {error}, status)
            }
        }
    }
}