package monstersirenfetch import ( "os" "strconv" ) // AlbumConflictError is returned by [Flatten] if two or more albums share the same CID. type AlbumConflictError struct { Index int Previous Album Current Album } func (e *AlbumConflictError) Error() string { return "album CID conflict on index " + strconv.Itoa(e.Index) + ": " + "previous album " + e.Previous.Name + ", " + "current album " + e.Current.Name } // SongConflictError is returned by [Flatten] if two or more songs belonging to the same [Album] share the same CID. type SongConflictError struct { Index int Previous *Song Current Song } func (e *SongConflictError) Error() string { return "song CID conflict on index " + strconv.Itoa(e.Index) + ": " + "previous song " + e.Previous.Name + ", " + "current song " + e.Current.Name } // CompositeAlbum represents an [Album] with a collection of its associated identifier-only [Song]. type CompositeAlbum struct { // Songs is a map of [Song.CID] to identifier-only [Song]. Songs map[StringInt]*Song *Album } // CompositeAlbumsMap is a map of [Album.CID] to [CompositeAlbum]. type CompositeAlbumsMap map[StringInt]CompositeAlbum // Flatten flattens [AlbumsData] and [SongsData] into a [CompositeAlbumsMap]. // All values in [CompositeAlbum.Songs] and the [CompositeAlbum.Album] field are guaranteed to be non-nil. func Flatten(albumData AlbumsData, songsData SongsData) (CompositeAlbumsMap, error) { m := make(CompositeAlbumsMap, len(albumData)) for i, a := range albumData { if c, ok := m[a.CID]; ok { return nil, &AlbumConflictError{Index: i, Previous: *c.Album, Current: a} } m[a.CID] = CompositeAlbum{Songs: make(map[StringInt]*Song), Album: &a} } for i, s := range songsData.List { var c *Song if a, ok := m[s.AlbumCID]; !ok { return nil, os.ErrNotExist } else if c, ok = a.Songs[s.CID]; ok { return nil, &SongConflictError{Index: i, Previous: c, Current: s} } else { a.Songs[s.CID] = &s } } return m, nil }