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
//! Apple music resources
use am_api_proc_macro::Context;

use serde::{Deserialize, Serialize};

pub mod artwork;
pub mod attributes;
pub mod catalog;
pub mod genre;
pub mod history;
pub mod library;
pub mod personal_recommendation;
pub mod rating;
pub mod relationship;
pub mod storefront;
pub mod view;

/// Apple music resource header
#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq, Eq, Hash)]
#[serde(rename_all = "camelCase", default)]
pub struct ResourceHeader {
    /// Identifier
    pub id: String,
    /// Relative location for the resource
    pub href: String,
}

/// Trait for getting resource information
pub trait ResourceInfo {
    /// Get header
    fn get_header(&self) -> &ResourceHeader;
}

/// Trait for getting resource data type
pub(crate) trait ResourceType {
    /// Get resource type
    fn get_type(&self) -> &'static str;
}

macro_rules! resource {
    ($($name:literal => $enum_name:ident : $data_type:path),*) => {
        /// Apple music resource data
        #[derive(Context, Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
        #[serde(tag = "type")]
        pub enum Resource {
            $(
                #[doc = $name]
                #[serde(rename = $name)]
                $enum_name {
                    /// Data
                    #[serde(flatten)]
                    data: $data_type
                }
            ),*
        }

        impl ResourceInfo for Resource {
            fn get_header(&self) -> &ResourceHeader {
                match self {
                    $(Self::$enum_name { data } => &data.header),*
                }
            }
        }

        impl ResourceType for Resource {
            fn get_type(&self) -> &'static str {
                match self {
                    $(Self::$enum_name { .. } => $name),*
                }
            }
        }

        $(
            impl From<$data_type> for Resource {
                fn from(data: $data_type) -> Self {
                    Self::$enum_name { data }
                }
            }
        )*
    }
}

resource! {
    "activities" => Activity : catalog::activity::Activity,
    "albums" => Album : catalog::album::Album,
    "artists" => Artist : catalog::artist::Artist,
    "apple-curators" => AppleCurator : catalog::curator::AppleCurator,
    "curators" => Curator : catalog::curator::Curator,
    "genres" => Genre : genre::Genre,
    "music-videos" => MusicVideo : catalog::music_video::MusicVideo,
    "personal-recommendation" => PersonalRecommendation : personal_recommendation::PersonalRecommendation,
    "playlists" => Playlist : catalog::playlist::Playlist,
    "ratings" => Rating : rating::Rating,
    "record-labels" => RecordLabel : catalog::record_label::RecordLabel,
    "songs" => Song : catalog::song::Song,
    "stations" => Station : catalog::station::Station,
    "station-genres" => StationGenre : catalog::station::StationGenre,
    "library-albums" => LibraryAlbum : library::album::LibraryAlbum,
    "library-artists" => LibraryArtist : library::artist::LibraryArtist,
    "library-music-videos" => LibraryMusicVideo : library::music_video::LibraryMusicVideo,
    "library-playlists" => LibraryPlaylist : library::playlist::LibraryPlaylist,
    "library-playlist-folders" => LibraryPlaylistFolder : library::playlist::LibraryPlaylistFolder,
    "library-songs" => LibrarySong : library::song::LibrarySong
}

/// Apple music response
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
#[serde(rename_all = "camelCase")]
pub struct ResourceResponse<R = Resource> {
    /// Data
    pub data: Vec<R>,
}

/// Apple music error response
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
#[serde(rename_all = "camelCase")]
pub struct ErrorResponse {
    /// Error code
    #[serde(default)]
    pub code: Option<i32>,
    /// Error message
    #[serde(default)]
    pub message: Option<String>,
    /// Errors
    #[serde(default)]
    pub errors: Vec<MusicError>,
}

/// Apple music error
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, Hash)]
#[serde(rename_all = "camelCase", default)]
pub struct MusicError {
    /// Error id
    pub id: String,
    /// Error title
    pub title: String,
    /// Error detail
    pub detail: String,
    /// Status
    pub status: String,
    /// Error code
    pub code: String,
}