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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
use crate::model::*;
use serde::{Serializer, ser::SerializeStruct};
// See line 50
//use chrono::serde::ts_milliseconds;

/// A device object.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Device {
    /// The device id. It can be None, and I don't know why.
    pub id: Option<String>,
    /// Whether this device is the currently active device.
    pub is_active: bool,
    /// Whether this device is currently in a private session.
    pub is_private_session: bool,
    /// Whether controlling this device is restricted; if set to true, no Web API commands will be
    /// accepted by it.
    pub is_restricted: bool,
    /// The name of the device.
    pub name: String,
    /// The type of the device.
    #[serde(rename = "type")]
    pub device_type: DeviceType,
    /// The current volume in percent. It can be None, and I don't know why.
    pub volume_percent: Option<u32>,
}

/// A type of device.
#[derive(Debug, Clone, PartialEq, Eq, Copy, Hash, Serialize, Deserialize)]
pub enum DeviceType {
    Computer,
    Tablet,
    Smartphone,
    Speaker,
    TV,
    AVR,
    STB,
    AudioDongle,
    GameConsole,
    CastVideo,
    CastAudio,
    Automobile,
    Unknown,
}

/// Information about the currently playing track.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CurrentlyPlaying {
    /// The context of the currently playing track. Is None for example if a private session is
    /// enabled.
    pub context: Option<Context>,
    // Spotify gave me negative timestamps for some reason so I had to disable this.
    // /// When data was fetched.
    // #[serde(with = "ts_milliseconds")]
    // pub timestamp: DateTime<Utc>,
    /// Progress into the currently playing track. Is None for example if a private session is
    /// enabled.
    #[serde(
        rename = "progress_ms",
        with = "serde_duration_millis_option"
    )]
    pub progress: Option<Duration>,
    /// If something is currently playing.
    pub is_playing: bool,
    /// The currently playing track. Is None for example if a private session is enabled.
    pub item: Option<Track>,
    /// The object type of the currently playing item.
    pub currently_playing_type: TrackType,
    /// Which actions are disallowed in the current context.
    pub actions: Actions,
}

/// Information about a user's current playback state.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CurrentPlayback {
    /// The currently active device.
    pub device: Device,
    /// The repeat state.
    pub repeat_state: RepeatState,
    /// Whether shuffle is on.
    pub shuffle_state: bool,
    /// The currently playing track.
    #[serde(flatten)]
    pub currently_playing: CurrentlyPlaying,
}

/// Actions that are disallowed in the current context.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Actions {
    #[serde(with = "serde_disallows")]
    pub disallows: Vec<Disallow>,
}

/// An action that is currently not able to be performed.
#[derive(Debug, Clone, PartialEq, Eq, Copy, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Disallow {
    InterruptingPlayback,
    Pausing,
    Resuming,
    Seeking,
    SkippingNext,
    SkippingPrev,
    TogglingRepeatContext,
    TogglingShuffle,
    TogglingRepeatTrack,
    TransferringPlayback,
}

/// The type of track.
#[derive(Debug, Clone, PartialEq, Eq, Copy, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum TrackType {
    Track,
    Episode,
    Ad,
    Unknown,
}

/// The context of the current playing track.
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
pub struct Context {
    /// The type of context; album, artist, playlist, track.
    #[serde(rename = "type")]
    pub context_type: ItemType,
    /// External URLs for this context.
    pub external_urls: HashMap<String, String>,
    /// The [Spotify
    /// ID](https://developer.spotify.com/documentation/web-api/#spotify-uris-and-ids)
    /// for the context.
    #[serde(rename = "uri", deserialize_with = "de_any_uri")]
    pub id: String,
}

impl Serialize for Context {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        let mut context = serializer.serialize_struct("Context", 3)?;
        context.serialize_field("type", &self.context_type)?;
        context.serialize_field("external_urls", &self.external_urls)?;
        context.serialize_field("uri", {
            struct UriSerialize<'a> {
                context_type: ItemType,
                id: &'a str,
            }
            impl Serialize for UriSerialize<'_> {
                fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
                    serializer.serialize_str(&format!("spotify:{}:{}", self.context_type.as_str(), self.id))
                }
            }
            &UriSerialize {
                context_type: self.context_type,
                id: &self.id
            }
        })?;
        context.end()
    }
}

/// Repeating the track, the context or not at all.
#[derive(Debug, Clone, PartialEq, Eq, Copy, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum RepeatState {
    /// Not repeating.
    Off,
    /// Repeating the current track.
    Track,
    /// Repeating the current context (e.g. playlist, album, etc).
    Context,
}

impl RepeatState {
    pub fn as_str(self) -> &'static str {
        match self {
            Self::Off => "off",
            Self::Track => "track",
            Self::Context => "context",
        }
    }
}