1
1
/* Spotify Web API - Kotlin Wrapper; MIT License, 2019; Original author: Adam Ratzman */
2
2
package com.adamratzman.spotify.models
3
3
4
+ import kotlinx.serialization.Decoder
5
+ import kotlinx.serialization.Encoder
6
+ import kotlinx.serialization.KSerializer
7
+ import kotlinx.serialization.SerialDescriptor
4
8
import kotlinx.serialization.Serializable
5
- import kotlinx.serialization.modules.SerializersModule
6
-
7
- internal val spotifyUriSerializersModule = SerializersModule {
8
- polymorphic(SpotifyUri ::class ) {
9
- AlbumUri ::class with AlbumUri .serializer()
10
- ArtistUri ::class with ArtistUri .serializer()
11
- TrackUri ::class with TrackUri .serializer()
12
- LocalTrackUri ::class with LocalTrackUri .serializer()
13
- UserUri ::class with UserUri .serializer()
14
- PlaylistUri ::class with PlaylistUri .serializer()
15
- }
16
- }
9
+ import kotlinx.serialization.Serializer
10
+ import kotlinx.serialization.internal.StringDescriptor
17
11
18
12
private fun String.matchType (type : String ): String? {
19
13
val typeRegex = " ^spotify:(?:.*:)*$type :([^:]+)(?::.*)*$|^([^:]+)$" .toRegex()
@@ -35,26 +29,27 @@ private fun String.remove(type: String): String {
35
29
throw SpotifyUriException (" Illegal Spotify ID/URI: '$this ' isn't convertible to '$type ' id" )
36
30
}
37
31
32
+ private class SimpleUriSerializer <T : SpotifyUri >(val ctor : (String ) -> T ) : KSerializer<T> {
33
+ override val descriptor: SerialDescriptor = StringDescriptor
34
+ override fun deserialize (decoder : Decoder ): T = ctor(decoder.decodeString())
35
+ override fun serialize (encoder : Encoder , obj : T ) = encoder.encodeString(obj.uri)
36
+ }
37
+
38
38
/* *
39
39
* Represents a Spotify URI, parsed from either a Spotify ID or taken from an endpoint.
40
40
*
41
41
* @property uri retrieve this URI as a string
42
42
* @property id representation of this uri as an id
43
43
*/
44
44
@Serializable
45
- sealed class SpotifyUri (val input : String , val type : UriType ) {
45
+ sealed class SpotifyUri (val input : String , type : String ) {
46
46
val uri: String
47
47
val id: String
48
48
49
49
init {
50
50
input.replace(" " , " " ).let {
51
- if (input == " spotify:user:" ) {
52
- this .uri = input
53
- this .id = input
54
- } else {
55
- this .uri = it.add(type.toString())
56
- this .id = it.remove(type.toString())
57
- }
51
+ this .uri = it.add(type)
52
+ this .id = it.remove(type)
58
53
}
59
54
}
60
55
@@ -73,60 +68,156 @@ sealed class SpotifyUri(val input: String, val type: UriType) {
73
68
return " SpotifyUri($uri )"
74
69
}
75
70
76
- enum class UriType (private val typeStr : String ) {
77
- ALBUM (" album" ),
78
- ARTIST (" artist" ),
79
- TRACK (" track" ),
80
- USER (" user" ),
81
- PLAYLIST (" playlist" ),
82
- LOCAL_TRACK (" local" );
71
+ @Serializer(forClass = SpotifyUri ::class )
72
+ companion object : KSerializer <SpotifyUri > {
73
+ override val descriptor: SerialDescriptor = StringDescriptor
74
+ override fun deserialize (decoder : Decoder ): SpotifyUri = SpotifyUri (decoder.decodeString())
75
+ override fun serialize (encoder : Encoder , obj : SpotifyUri ) = encoder.encodeString(obj.uri)
76
+
77
+ /* *
78
+ * This function safely instantiates a SpotifyUri from given constructor.
79
+ * */
80
+ inline fun <T : SpotifyUri > safeInitiate (uri : String , ctor : (String ) -> T ): T ? {
81
+ return try {
82
+ ctor(uri)
83
+ } catch (e: SpotifyUriException ) {
84
+ null
85
+ }
86
+ }
87
+
88
+ /* *
89
+ * Creates a abstract SpotifyUri of given input. Doesn't allow ambiguity by disallowing creation by id.
90
+ * */
91
+ operator fun invoke (input : String ): SpotifyUri {
92
+ val constructors = listOf (::AlbumUri , ::ArtistUri , TrackUri .Companion ::invoke, ::UserUri , ::PlaylistUri )
93
+ for (ctor in constructors) {
94
+ safeInitiate(input, ctor)?.takeIf { it.uri == input }?.also { return it }
95
+ }
83
96
84
- override fun toString () = typeStr
85
- }
97
+ throw SpotifyUriException ( " Illegal Spotify ID/URI: ' $input ' isn't convertible to any arbitrary id " )
98
+ }
86
99
87
- companion object {
88
- fun isUriType (uri : String , type : UriType ) = uri.matchType(type.toString()) != null
100
+ /* *
101
+ * This function returns whether or not the given input IS a given type.
102
+ *
103
+ * @example ```Kotlin
104
+ * SpotifyUri.isType<UserUri>("abc") // returns: false
105
+ * SpotifyUri.isType<UserUri>("spotify:user:abc") // returns: true
106
+ * SpotifyUri.isType<UserUri>("spotify:track:abc") // returns: false
107
+ * ```
108
+ * */
109
+ inline fun <reified T : SpotifyUri > isType (input : String ): Boolean {
110
+ return safeInitiate(input, ::invoke)?.let { it is T } ? : false
111
+ }
112
+
113
+ /* *
114
+ * This function returns whether ot not the given input CAN be a given type.
115
+ *
116
+ * @example ```Kotlin
117
+ * SpotifyUri.canBeType<UserUri>("abc") // returns: true
118
+ * SpotifyUri.canBeType<UserUri>("spotify:user:abc") // returns: true
119
+ * SpotifyUri.canBeType<UserUri>("spotify:track:abc") // returns: false
120
+ * ```
121
+ * */
122
+ inline fun <reified T : SpotifyUri > canBeType (input : String ): Boolean {
123
+ return isType<T >(input) || ! input.contains(' :' )
124
+ }
89
125
}
90
126
}
91
127
92
128
/* *
93
129
* Represents a Spotify **Album** URI, parsed from either a Spotify ID or taken from an endpoint.
94
130
*/
95
131
@Serializable
96
- class AlbumUri (private val inputString : String ) : SpotifyUri(inputString, UriType .ALBUM )
132
+ class AlbumUri (input : String ) : SpotifyUri(input, " album" ) {
133
+ @Serializer(forClass = AlbumUri ::class )
134
+ companion object : KSerializer <AlbumUri > by SimpleUriSerializer (::AlbumUri )
135
+ }
136
+
137
+ @Deprecated(" renamed" , ReplaceWith (" AlbumUri" , " com.adamratzman.spotify.models.AlbumUri" ))
97
138
typealias AlbumURI = AlbumUri
98
139
99
140
/* *
100
141
* Represents a Spotify **Artist** URI, parsed from either a Spotify ID or taken from an endpoint.
101
142
*/
102
143
@Serializable
103
- class ArtistUri (private val inputString : String ) : SpotifyUri(inputString, UriType .ARTIST )
104
- typealias ArtistURI = ArtistUri
144
+ class ArtistUri (input : String ) : SpotifyUri(input, " artist" ) {
145
+ @Serializer(forClass = ArtistUri ::class )
146
+ companion object : KSerializer <ArtistUri > by SimpleUriSerializer (::ArtistUri )
147
+ }
105
148
106
- /* *
107
- * Represents a Spotify **Track** URI, parsed from either a Spotify ID or taken from an endpoint.
108
- */
109
- @Serializable
110
- class TrackUri (private val inputString : String ) : SpotifyUri(inputString, UriType .TRACK )
111
- typealias TrackURI = TrackUri
149
+ @Deprecated(" renamed" , ReplaceWith (" ArtistUri" , " com.adamratzman.spotify.models.ArtistUri" ))
150
+ typealias ArtistURI = ArtistUri
112
151
113
152
/* *
114
153
* Represents a Spotify **User** URI, parsed from either a Spotify ID or taken from an endpoint.
115
154
*/
116
155
@Serializable
117
- class UserUri (private val inputString : String ) : SpotifyUri(inputString, UriType .USER )
156
+ class UserUri (input : String ) : SpotifyUri(input, " user" ) {
157
+ @Serializer(forClass = UserUri ::class )
158
+ companion object : KSerializer <UserUri > by SimpleUriSerializer (::UserUri )
159
+ }
160
+
161
+ @Deprecated(" renamed" , ReplaceWith (" UserUri" , " com.adamratzman.spotify.models.UserUri" ))
118
162
typealias UserURI = UserUri
119
163
120
164
/* *
121
165
* Represents a Spotify **Playlist** URI, parsed from either a Spotify ID or taken from an endpoint.
122
166
*/
123
167
@Serializable
124
- class PlaylistUri (private val inputString : String ) : SpotifyUri(inputString, UriType .PLAYLIST )
168
+ class PlaylistUri (input : String ) : SpotifyUri(input, " playlist" ) {
169
+ @Serializer(forClass = PlaylistUri ::class )
170
+ companion object : KSerializer <PlaylistUri > by SimpleUriSerializer (::PlaylistUri )
171
+ }
172
+
173
+ @Deprecated(" renamed" , ReplaceWith (" PlaylistUri" , " com.adamratzman.spotify.models.PlaylistUri" ))
125
174
typealias PlaylistURI = PlaylistUri
126
175
176
+ /* *
177
+ * Represents a Spotify **Track** URI, ether LocalTrack or SpotifyTrack, parsed from either a Spotify ID or taken
178
+ * from an endpoint
179
+ * */
180
+ @Serializable
181
+ sealed class TrackUri (input : String , type : String ) : SpotifyUri(input, type) {
182
+ @Serializer(forClass = TrackUri ::class )
183
+ companion object : KSerializer <TrackUri > {
184
+ override val descriptor: SerialDescriptor = StringDescriptor
185
+ override fun deserialize (decoder : Decoder ): TrackUri = TrackUri (decoder.decodeString())
186
+ override fun serialize (encoder : Encoder , obj : TrackUri ) = encoder.encodeString(obj.uri)
187
+
188
+ /* *
189
+ * Creates a abstract TrackURI of given input. Prefers SpotifyTrackUri if the input is ambiguous.
190
+ * */
191
+ operator fun invoke (input : String ): TrackUri {
192
+ val constructors = listOf (::SpotifyTrackUri , ::LocalTrackUri )
193
+ for (ctor in constructors) {
194
+ safeInitiate(input, ctor)?.also { return it }
195
+ }
196
+ throw SpotifyUriException (" Illegal Spotify ID/URI: '$input ' isn't convertible to 'track' or 'local' id" )
197
+ }
198
+ }
199
+ }
200
+
201
+ @Deprecated(" renamed" , ReplaceWith (" TrackUri" , " com.adamratzman.spotify.models.TrackUri" ))
202
+ typealias TrackURI = TrackUri
203
+
204
+ /* *
205
+ * Represents a Spotify **Track** URI, parsed from either a Spotify ID or taken from an endpoint.
206
+ */
207
+ @Serializable
208
+ class SpotifyTrackUri (input : String ) : TrackUri(input, " track" ) {
209
+ @Serializer(forClass = SpotifyTrackUri ::class )
210
+ companion object : KSerializer <SpotifyTrackUri > by SimpleUriSerializer (::SpotifyTrackUri )
211
+ }
212
+
127
213
/* *
128
214
* Represents a Spotify **local track** URI
129
215
*/
130
216
@Serializable
131
- class LocalTrackUri (private val inputString : String ) : SpotifyUri(inputString, UriType .LOCAL_TRACK )
217
+ class LocalTrackUri (input : String ) : TrackUri(input, " local" ) {
218
+ @Serializer(forClass = LocalTrackUri ::class )
219
+ companion object : KSerializer <LocalTrackUri > by SimpleUriSerializer (::LocalTrackUri )
220
+ }
221
+
222
+ @Deprecated(" renamed" , ReplaceWith (" LocalTrackUri" , " com.adamratzman.spotify.models.LocalTrackUri" ))
132
223
typealias LocalTrackURI = LocalTrackUri
0 commit comments