mirror of
https://github.com/ramsayleung/rspotify.git
synced 2026-04-26 07:55:55 +03:00
[PR #243] [CLOSED] Fix IDs v3 #337
Labels
No labels
Stale
bug
discussion
enhancement
good first issue
good first issue
help wanted
pull-request
question
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
starred/rspotify#337
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
📋 Pull Request Information
Original PR: https://github.com/ramsayleung/rspotify/pull/243
Author: @marioortizmanero
Created: 8/25/2021
Status: ❌ Closed
Base:
master← Head:fix-ids-3📝 Commits (10+)
c2046c7Add UnknownId and some testsd5d2812Proper docs thanks to reworked macro5a52393Module-level documentation1ab8ad1Now with an example!3b66b9aStuck fighting compiler error about Deserialize5d459f4First draft with newtypeea435dbThe compiler hates mece0a860Mixing Attempt #1 and Attempt #2bbf0cc2Almost working now5b1562fFinally fix Offset impl📊 Changes
21 files changed (+564 additions, -434 deletions)
View changed files
📝
rspotify-model/src/album.rs(+6 -10)📝
rspotify-model/src/artist.rs(+4 -7)📝
rspotify-model/src/audio.rs(+4 -3)📝
rspotify-model/src/category.rs(+2 -2)📝
rspotify-model/src/context.rs(+11 -9)📝
rspotify-model/src/enums/types.rs(+1 -0)📝
rspotify-model/src/idtypes.rs(+426 -189)📝
rspotify-model/src/lib.rs(+21 -18)📝
rspotify-model/src/offset.rs(+4 -4)📝
rspotify-model/src/playing.rs(+2 -3)📝
rspotify-model/src/playlist.rs(+4 -8)📝
rspotify-model/src/recommend.rs(+3 -3)📝
rspotify-model/src/search.rs(+5 -6)📝
rspotify-model/src/show.rs(+7 -11)📝
rspotify-model/src/track.rs(+6 -19)📝
rspotify-model/src/user.rs(+3 -6)📝
src/clients/mod.rs(+3 -6)📝
src/clients/oauth.rs(+12 -10)📝
src/lib.rs(+1 -0)📝
tests/test_models.rs(+2 -78)...and 1 more files
📄 Description
Description
Closes #203
This final approach consists of mixing two of the ideas I explained in #203.
Explanation
It is fundamentally impossible to have an object-safe
Idtrait because it requires aconstfield in one way or another that links its type to the variant inType. So attempting to usedynfor runtime usage (Box<dyn Id>orVec<Box<dyn Id>>) has to be discarded. Thus, I've created IDs that may hold multiple types instead of a specific one;AnyIdserves for any kind of Id,PlayableIdcan be created with aTrackIdor anEpisodeId, etc. Note that these are types and not traits. So when a function wants to take different kinds of IDs, it's not done via generics; it's done via ID conversions.Say for example you have a
tracks: Vec<TrackId>and the function wants aVec<PlayableId>. In that case, you'd simply pass your tracks withtracks.into_iter().map(|t| t.as_ref()).collect(). Note that we useAsReffor conversions instead ofFrom/Intobecause it's a cheap conversion; it only requires a pointer cast (that doesn't even useunsafe).In order to make the previous conversion possible, one has to implement
impl AsRef<Id<Playable>> for Id<Track>. The problem here is that after allId<Playable>andId<Track>are the same type. It's just the generics that change. So thisimplblock would conflict withimpl AsRef<T> for T, which already exists in the standard library, and makes it impossible.Given the previous reasoning, we know that each Id must be a different type, discarding
Id<T>. The only remaining option here is to have astructfor each Id which implements theIdtrait with all of its functionality. Thus, for each kind of ID we need to:Id,AsRef...)Which is a lot of boilerplate when we have 2+ kinds of IDs. To make it a bit simpler, we can use both macros and blanket implementations. Ideally, we just need:
Idtrait, and same forIdBuf:define_idtypes!define_one_way_conversions!AsRef,Deref...)The only issue here is that the blanket implementations don't work; you can't just
impl<T: Id> AsRef<AnyId> for TbecauseTmay be from a foreign crate, just likeAsRef. And as we all know that is not allowed by Rust. So in the end we have to make theseimplin the first macro instead of with blanket implementations. Which is not really a problem, but large macros are scary.Even though this explanation is quite complex and may be hard to follow, the
idtypesmodule isn't really that hard to understand. Here I just tried to explain in detail why this is the only way to do it, and how it works. But the user only needs to know that there are different types to represent IDs, even groups of types, and that they can freely convert from single types into groups of types.Disadvantages
The main disadvantages are:
Id::from_uri(...)won't work.TrackIdintoPlayableId, it is impossible to turn it back intoTrackId.But this is literally the only way I've found to fix IDs after lots of different approaches and time invested. Please, if there's anything unclear just ask, and if you find anything with room improvement let me know.
🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.