/* Copyright © 2023 Philipp Wolfer This file is part of Scotty. Scotty is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Scotty is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Scotty. If not, see . */ package backends import ( "errors" "fmt" "reflect" "strings" "github.com/spf13/viper" "go.uploadedlobster.com/scotty/internal/backends/deezer" "go.uploadedlobster.com/scotty/internal/backends/dump" "go.uploadedlobster.com/scotty/internal/backends/funkwhale" "go.uploadedlobster.com/scotty/internal/backends/jspf" "go.uploadedlobster.com/scotty/internal/backends/lastfm" "go.uploadedlobster.com/scotty/internal/backends/listenbrainz" "go.uploadedlobster.com/scotty/internal/backends/maloja" "go.uploadedlobster.com/scotty/internal/backends/scrobblerlog" "go.uploadedlobster.com/scotty/internal/backends/spotify" "go.uploadedlobster.com/scotty/internal/backends/subsonic" "go.uploadedlobster.com/scotty/internal/models" ) type BackendInfo struct { Name string ExportCapabilities []Capability ImportCapabilities []Capability } type Capability = string func ResolveBackend[T interface{}](config *viper.Viper) (T, error) { backendName, backend, err := resolveBackend(config) var result T if err != nil { return result, err } implements, interfaceName := ImplementsInterface[T](&backend) if implements { result = backend.(T) } else { err = errors.New( fmt.Sprintf("Backend %s does not implement %s", backendName, interfaceName)) } return result, err } func GetBackends() []BackendInfo { backends := make([]BackendInfo, 0) for name, backendFunc := range knownBackends { backend := backendFunc() info := BackendInfo{ Name: name, ExportCapabilities: getExportCapabilities(backend), ImportCapabilities: getImportCapabilities(backend), } backends = append(backends, info) } return backends } var knownBackends = map[string]func() models.Backend{ "deezer": func() models.Backend { return &deezer.DeezerApiBackend{} }, "dump": func() models.Backend { return &dump.DumpBackend{} }, "funkwhale": func() models.Backend { return &funkwhale.FunkwhaleApiBackend{} }, "jspf": func() models.Backend { return &jspf.JSPFBackend{} }, "lastfm": func() models.Backend { return &lastfm.LastfmApiBackend{} }, "listenbrainz": func() models.Backend { return &listenbrainz.ListenBrainzApiBackend{} }, "maloja": func() models.Backend { return &maloja.MalojaApiBackend{} }, "scrobbler-log": func() models.Backend { return &scrobblerlog.ScrobblerLogBackend{} }, "spotify": func() models.Backend { return &spotify.SpotifyApiBackend{} }, "subsonic": func() models.Backend { return &subsonic.SubsonicApiBackend{} }, } func resolveBackend(config *viper.Viper) (string, models.Backend, error) { backendName := config.GetString("backend") backendType := knownBackends[backendName] if backendType == nil { return backendName, nil, fmt.Errorf("Unknown backend %s", backendName) } return backendName, backendType().FromConfig(config), nil } func ImplementsInterface[T interface{}](backend *models.Backend) (bool, string) { expectedInterface := reflect.TypeOf((*T)(nil)).Elem() implements := backend != nil && reflect.TypeOf(*backend).Implements(expectedInterface) return implements, expectedInterface.Name() } func getExportCapabilities(backend models.Backend) []string { caps := make([]Capability, 0) var name string var found bool name, found = checkCapability[models.ListensExport](backend, "export") if found { caps = append(caps, name) } name, found = checkCapability[models.LovesExport](backend, "export") if found { caps = append(caps, name) } return caps } func getImportCapabilities(backend models.Backend) []Capability { caps := make([]Capability, 0) var name string var found bool name, found = checkCapability[models.ListensImport](backend, "import") if found { caps = append(caps, name) } name, found = checkCapability[models.LovesImport](backend, "import") if found { caps = append(caps, name) } return caps } func checkCapability[T interface{}](backend models.Backend, suffix string) (string, bool) { implements, name := ImplementsInterface[T](&backend) if implements { cap, found := strings.CutSuffix(strings.ToLower(name), suffix) if found { return cap, found } } return "", false }