song: zero fields for variants

Once again, helps remove code duplication and be more future-proof in
case any more variants are uncovered.

Signed-off-by: Yonah <contrib@gensokyo.uk>
This commit is contained in:
Yonah 2025-09-18 20:36:34 +09:00
parent a63af7a4fb
commit 526a0371a4
Signed by: yonah
SSH Key Fingerprint: SHA256:vnQvK8+XXH9Tbni2AV1a/8qdVK/zPcXw52GM0ruQvwA
2 changed files with 105 additions and 2 deletions

47
song.go
View File

@ -13,7 +13,6 @@ import (
type SongResponse Response[Song]
// Song holds the metadata of a song.
// Fields marked with omitempty are only populated when the IsFull method returns true.
type Song struct {
CID StringInt `json:"cid"`
Name string `json:"name"`
@ -25,9 +24,53 @@ type Song struct {
Artists []string `json:"artists"`
}
// IsFull returns whether the metadata held by [Song] is considered full (originating from a [SongResponse]).
const (
// SongVariantCurrent copies the current variant as-is.
SongVariantCurrent = iota
// SongVariantFull leaves all fields intact in the copy.
// This variant is returned by /api/song/%d.
SongVariantFull
// SongVariantBase zeroes [Song.SourceURL], [Song.LyricURL], [Song.MvURL], [Song.MvCoverURL].
// This variant is included in /api/songs.
SongVariantBase
)
// IsFull returns whether [Song] is considered to be its [SongVariantFull] variant.
func (s *Song) IsFull() bool { return s.SourceURL != "" }
// Copy makes a copy of [Song].
// For a [Song] where the IsFull method returns true, Copy zeroes fields to convert the copy into other variants.
// For [Song] where IsFull returns false, any variant other than [SongVariantCurrent] is undefined.
func (s *Song) Copy(variant int) *Song {
if s == nil {
return nil
}
v := *s
if variant == SongVariantCurrent {
return &v
}
if !s.IsFull() {
return nil
}
switch variant {
case SongVariantFull:
break
case SongVariantBase:
s.SourceURL = ""
s.LyricURL = ""
s.MvURL = ""
s.MvCoverURL = ""
default:
return nil
}
return s
}
// Enrich populates the remaining fields of a non-full [Song].
func (s *Song) Enrich(ctx context.Context, n Net) error {
if s == nil || s.IsFull() {

View File

@ -22,6 +22,66 @@ func TestSong(t *testing.T) {
LyricURL: "https://web.hycdn.cn/siren/lyric/20240709/4a10c70629b68a187fdbef4a27bd32d8.lrc",
Artists: []string{"塞壬唱片-MSR"},
}}, songJSON)
t.Run("copy", func(t *testing.T) {
testCases := []struct {
name string
a *Song
variant int
want *Song
}{
{"nil", nil, SongVariantBase, nil},
{"current", new(Song), SongVariantCurrent, new(Song)},
{"full guard", new(Song), SongVariantFull, nil},
{"current full", &Song{
SourceURL: "\x00",
}, SongVariantCurrent, &Song{
SourceURL: "\x00",
}},
{"oob", &Song{
SourceURL: "\x00",
}, 0xbad, nil},
{"full", &Song{
CID: 48794,
Name: "Warm and Small Light",
AlbumCID: 6660,
SourceURL: "https://res01.hycdn.cn/04ce5de54bb52eb85008644d541d40fa/68CA0442/siren/audio/20240709/a7f650238eaefc9c30a9627d7f78d819.wav",
LyricURL: "https://web.hycdn.cn/siren/lyric/20240709/4a10c70629b68a187fdbef4a27bd32d8.lrc",
Artists: []string{"塞壬唱片-MSR"},
}, SongVariantFull, &Song{
CID: 48794,
Name: "Warm and Small Light",
AlbumCID: 6660,
SourceURL: "https://res01.hycdn.cn/04ce5de54bb52eb85008644d541d40fa/68CA0442/siren/audio/20240709/a7f650238eaefc9c30a9627d7f78d819.wav",
LyricURL: "https://web.hycdn.cn/siren/lyric/20240709/4a10c70629b68a187fdbef4a27bd32d8.lrc",
Artists: []string{"塞壬唱片-MSR"},
}},
{"base", &Song{
CID: 48794,
Name: "Warm and Small Light",
AlbumCID: 6660,
SourceURL: "https://res01.hycdn.cn/04ce5de54bb52eb85008644d541d40fa/68CA0442/siren/audio/20240709/a7f650238eaefc9c30a9627d7f78d819.wav",
LyricURL: "https://web.hycdn.cn/siren/lyric/20240709/4a10c70629b68a187fdbef4a27bd32d8.lrc",
Artists: []string{"塞壬唱片-MSR"},
}, SongVariantBase, &Song{
CID: 48794,
Name: "Warm and Small Light",
AlbumCID: 6660,
Artists: []string{"塞壬唱片-MSR"},
}},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
if got := tc.a.Copy(tc.variant); !reflect.DeepEqual(got, tc.want) {
t.Errorf("Copy: %#v, want %#v", got, tc.want)
}
})
}
})
}
func TestSongEnrich(t *testing.T) {