@@ -24,15 +24,20 @@ private fun String.matchType(type: String, allowColon: Boolean): String? {
2424 return match[1 ].takeIf { it.isNotBlank() || match[2 ].isEmpty() } ? : match[2 ].takeIf { it.isNotEmpty() }
2525}
2626
27+ private fun String.matchesUserCollectionUri () = this .matches(" ^spotify:user:([^:]+):collection" .toRegex())
28+
2729private fun String.add (type : String , allowColon : Boolean ): String {
28- this .matchType(type, allowColon)?.let {
30+ if (type == UserCollectionUriType && matchesUserCollectionUri()) return this
31+ else this .matchType(type, allowColon)?.let {
2932 return " spotify:$type :${it.trim()} "
3033 }
3134 throw SpotifyUriException (" Illegal Spotify ID/URI: '$this ' isn't convertible to '$type ' uri" )
3235}
3336
3437private fun String.remove (type : String , allowColon : Boolean ): String {
35- this .matchType(type, allowColon)?.let {
38+ println (type)
39+ if (type == UserCollectionUriType && matchesUserCollectionUri()) return " collection"
40+ else this .matchType(type, allowColon)?.let {
3641 return it.trim()
3742 }
3843 throw SpotifyUriException (" Illegal Spotify ID/URI: '$this ' isn't convertible to '$type ' id" )
@@ -112,7 +117,7 @@ public sealed class SpotifyUri(input: String, public val type: String, allowColo
112117 val constructors = listOf (
113118 ::ArtistUri ,
114119 PlayableUri .Companion ::invoke,
115- ImmutableCollectionUri .Companion ::invoke,
120+ CollectionUri .Companion ::invoke,
116121 ::UserUri ,
117122 ::PlaylistUri
118123 )
@@ -178,7 +183,7 @@ public sealed class CollectionUri(input: String, type: String, allowColon: Boole
178183 * Creates an abstract [CollectionUri] of given input. Prefers [PlaylistUri] if the input is ambiguous.
179184 */
180185 public operator fun invoke (input : String ): CollectionUri {
181- val constructors = listOf (::PlaylistUri , ImmutableCollectionUri .Companion ::invoke)
186+ val constructors = listOf (::PlaylistUri , :: UserCollectionUri , ImmutableCollectionUri .Companion ::invoke)
182187 for (ctor in constructors) {
183188 safeInitiate(input, ctor)?.also { return it }
184189 }
@@ -372,9 +377,24 @@ public object ShowUriSerializer : KSerializer<ShowUri> by SimpleUriSerializer(::
372377 */
373378public fun String.toShowUri (): ShowUri = ShowUri (this )
374379
380+ private const val UserCollectionUriType = " UserCollectionUri"
381+
382+ /* *
383+ * Represents a Spotify **User Collection URI** URI (spotify:user:XXXX:collection), parsed from either a Spotify ID or taken from an endpoint.
384+ * It appears that this URI corresponds to the user's saved tracks collection in their library.
385+ */
386+ @Serializable(with = UserCollectionUriSerializer ::class )
387+ public class UserCollectionUri (input : String ) : CollectionUri(input, UserCollectionUriType ), ContextUri
388+ public object UserCollectionUriSerializer : KSerializer<UserCollectionUri> by SimpleUriSerializer(::UserCollectionUri )
389+
375390/* *
376- * Represents a Spotify **Context** URI (one of [AlbumUri], [ArtistUri], [PlaylistUri], or [ShowUri]),
377- */
391+ * Convert a show id or uri string to a [ShowUri] object
392+ */
393+ public fun String.toUserCollectionUri (): UserCollectionUri = UserCollectionUri (this )
394+
395+ /* *
396+ * Represents a Spotify **Context** URI (one of [AlbumUri], [ArtistUri], [PlaylistUri], [UserCollectionUri], or [ShowUri]),
397+ */
378398@Serializable(with = ContextUriSerializer ::class )
379399public interface ContextUri : ISpotifyUri {
380400 public companion object {
0 commit comments