147 lines
3.4 KiB
Go

package arn
import (
"errors"
"sort"
"sync"
"github.com/aerogo/nano"
"github.com/akyoto/color"
)
// AnimeCharacters is a list of characters for an anime.
type AnimeCharacters struct {
AnimeID AnimeID `json:"animeId" primary:"true"`
Items []*AnimeCharacter `json:"items" editable:"true"`
sync.Mutex
}
// Anime returns the anime the characters refer to.
func (characters *AnimeCharacters) Anime() *Anime {
anime, _ := GetAnime(characters.AnimeID)
return anime
}
// Add adds an anime character to the list.
func (characters *AnimeCharacters) Add(animeCharacter *AnimeCharacter) error {
if animeCharacter.CharacterID == "" || animeCharacter.Role == "" {
return errors.New("Empty ID or role")
}
characters.Lock()
characters.Items = append(characters.Items, animeCharacter)
characters.Unlock()
return nil
}
// FindByMapping finds an anime character by the given mapping.
func (characters *AnimeCharacters) FindByMapping(service string, serviceID string) *AnimeCharacter {
characters.Lock()
defer characters.Unlock()
for _, animeCharacter := range characters.Items {
character := animeCharacter.Character()
if character == nil {
color.Red("Anime %s has an incorrect Character ID inside AnimeCharacter: %s", characters.AnimeID, animeCharacter.CharacterID)
continue
}
if character.GetMapping(service) == serviceID {
return animeCharacter
}
}
return nil
}
// Link returns the link for that object.
func (characters *AnimeCharacters) Link() string {
return "/anime/" + characters.AnimeID + "/characters"
}
// String implements the default string serialization.
func (characters *AnimeCharacters) String() string {
return characters.Anime().String()
}
// GetID returns the anime ID.
func (characters *AnimeCharacters) GetID() string {
return characters.AnimeID
}
// TypeName returns the type name.
func (characters *AnimeCharacters) TypeName() string {
return "AnimeCharacters"
}
// Self returns the object itself.
func (characters *AnimeCharacters) Self() Loggable {
return characters
}
// Sort sorts the characters by role.
func (characters *AnimeCharacters) Sort() {
characters.Lock()
defer characters.Unlock()
sort.Slice(characters.Items, func(i, j int) bool {
// A little trick because "main" < "supporting"
return characters.Items[i].Role < characters.Items[j].Role
})
}
// Contains tells you whether the given character ID exists.
func (characters *AnimeCharacters) Contains(characterID string) bool {
characters.Lock()
defer characters.Unlock()
for _, item := range characters.Items {
if item.CharacterID == characterID {
return true
}
}
return false
}
// First gives you the first "count" anime characters.
func (characters *AnimeCharacters) First(count int) []*AnimeCharacter {
characters.Lock()
defer characters.Unlock()
if count > len(characters.Items) {
count = len(characters.Items)
}
return characters.Items[:count]
}
// GetAnimeCharacters ...
func GetAnimeCharacters(animeID AnimeID) (*AnimeCharacters, error) {
obj, err := DB.Get("AnimeCharacters", animeID)
if err != nil {
return nil, err
}
return obj.(*AnimeCharacters), nil
}
// StreamAnimeCharacters returns a stream of all anime characters.
func StreamAnimeCharacters() <-chan *AnimeCharacters {
channel := make(chan *AnimeCharacters, nano.ChannelBufferSize)
go func() {
for obj := range DB.All("AnimeCharacters") {
channel <- obj.(*AnimeCharacters)
}
close(channel)
}()
return channel
}