incomplete attributes in metadata structs #1054
Replies: 2 comments 5 replies
-
This is indeed an issue with no obvious good solution. I think the fact that the same protobuf definitions are used for different responses, containing different data is not great, but I guess that is something that every implementation of the Spotify API will have to deal with. In general I personally believe that forcing a check is better than silently using a default value for missing fields. When a programmer has to check if a value is present, they can decide to use a default value themselve or take another action. In contrast to that, a default value can be indistinguishable from a real value (e.g. default The issue of a program breaking when spotify changes data is not a big issue in my opinion. If not properly prepared, the program will stop to work the way it was intended in any case, but with explicit checks you will know about it or even be able to prevent it. In the other case it will just continue to go on with default values that may or may not work. So with Further explanation of Solution 3The idea of this proposal was to create an
This could look something like this (example for enum LazyArtist {
Minimal {
id: SpotifyId,
name: Option<String>,
},
Full(Box<Artist>),
} Performing a query to the endpoint dedicated to a given type, would always return the This does in my opinion provide a somewhat cleaner Api, but it of course has several disadvantages.
While Solution 3 was my proposal, I am really not sure which of the suggested solutions would be the best. All of them have advantages and disadvantages, which makes a decision very hard. |
Beta Was this translation helpful? Give feedback.
-
Thanks so much for this most excellent write-up and subsequent replies. Just to entertain my earlier thought (without saying I want to steer in that direction per se) could we not provide some macros to make life easy on us (so doing a |
Beta Was this translation helpful? Give feedback.
-
Recently, there have been several discussions about the current structs in the
librespot_metadata
crate. Since I have been involved in some of them myself, I thought I'd just write down the issue and some of my thoughts in a central place, as a starting point for future discussion.As far as I can see, two problems have been popping up with the current implementation:
Problem 1: missing attributes in API responses
Depending on the way a specific metadata struct has been instantiated, the degree to which attributes do actually have sensible values varies widely, and rarely all fields are covered.
One such example is
metadata::album::Album
struct:A request to
/metadata/4/album/<id>
returns quite some info; some attributes are still missing, but many are there:When requesting a track via
/metadata/4/track/<id>
however, which contains information about its containing album as well, we receive less information:The issue here is now that both are internally parsed with the same
.proto
definitions (protocol::metadata::Album
) and end up in the same higher-level struct from the metadata crate (metadata::album::Album
). In the latter, most attributes are filled with default values, as you can see below (this is the album data from the requested track parsed intometadata::album::Album
):This causes several problems:
Problem 2: Large struct sizes
As discovered here, e.g.
metadata::track::Track
is almost 1 KB on the stack, without all the possible data that a populatedTrack
struct would need additionally on the heap.Some data:
This is, of course, not generally a bad thing, it only becomes one, when most of these fields aren't actually filled with sensible data and still take up all the space.
Solution Idea 1:
Option<AllTheThings>
As proposed by @roderickvd here (and apparently already discussed somewhere else before), all
struct
attributes that are optional in the protobuf definitions or appear to be sometimes available and sometimes not could be madeOption<T>
s.This would solve several pain points with "Problem 1". However, the structs would still require as much space as before and some new problems might arise: Code that knows of the existence of optional fields now has to either
.unwrap()
them ormatch
them. The former can lead to surprising breakage, when Spotify changes the data they sent and the latter would introduce quite some boilerplate in library and user code.Solution Idea 2:
struct
s for everyone and everythingAs the name size, this proposal would mean to add separate high-level (as in: contained in the
metadata
crate) structs for each and every situation that returns different attributes.Something similar does already exist in
ArtistWithRole
, which is separate fromArtist
. In this case though, the underlying protobuf definition is also different (protocol::metadata::ArtistWithRole
).Where it is done exactly like that is the
rspotify
crate, e.g. for different album representations.While this might be the ideal solution from a library user's point of view, it potentially comes with a heavy burden for
librespot
developers.Although in contrast to the
.unwrap()
in "solution 1", the metadata would not break immediately in most cases, since they are still provided with defaults from the protobufs.Solution Idea 3:
enum
sI haven't yet thought about it more, but @dnlmlr also proposed something with regards to this issue. Since I'm not sure I understand it well enough to write down the details here myself, here's the link with his explanation: #1018 (comment)
I hope that this discussion is somehow useful to bundle the various talks into one single place. Looking forward to hearing your thoughts on the matter!
Beta Was this translation helpful? Give feedback.
All reactions