The composite maps are no longer needed as the new Album variant makes more sense than them. They are also unordered while the base variants of both endpoints are ordered. Composite is therefore only used for validation in the current implementation. Signed-off-by: Yonah <contrib@gensokyo.uk>
106 lines
2.4 KiB
Go
106 lines
2.4 KiB
Go
package monstersirenfetch
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
APIPrefix = "https://monster-siren.hypergryph.com/api"
|
|
)
|
|
|
|
// InconsistentEnrichError is returned by an Enrich method when a field is inconsistent
|
|
// between base data and enrichment data.
|
|
type InconsistentEnrichError[T any] struct {
|
|
Field string
|
|
Value T
|
|
NewValue T
|
|
}
|
|
|
|
func (e *InconsistentEnrichError[T]) Error() string {
|
|
return "field " + e.Field + " inconsistent: " +
|
|
fmt.Sprintf("%v differs from %v", e.Value, e.NewValue)
|
|
}
|
|
|
|
// Net represents an abstraction over the [net] package.
|
|
type Net interface {
|
|
// Get makes a get request to url and returns the response body.
|
|
// The caller must close the body after it finishes reading from it.
|
|
Get(ctx context.Context, url string) (body io.ReadCloser, contentLength int64, err error)
|
|
}
|
|
|
|
// Metadata represents metadata, often enriched, of the entire website.
|
|
// This is only used externally and is never returned by the API.
|
|
type Metadata struct {
|
|
Albums []Album `json:"albums"`
|
|
Songs []Song `json:"songs"`
|
|
}
|
|
|
|
// Response is a generic API response.
|
|
type Response[T any] struct {
|
|
Code int `json:"code"`
|
|
Message string `json:"msg"`
|
|
Data T `json:"data"`
|
|
}
|
|
|
|
// NullableString is a JSON string where its zero value behaves like null.
|
|
type NullableString string
|
|
|
|
func (s *NullableString) MarshalJSON() ([]byte, error) {
|
|
if *s == "" {
|
|
return []byte("null"), nil
|
|
}
|
|
return json.Marshal(string(*s))
|
|
}
|
|
|
|
func (s *NullableString) UnmarshalJSON(data []byte) (err error) {
|
|
var v *string
|
|
err = json.Unmarshal(data, &v)
|
|
if err == nil {
|
|
if v != nil {
|
|
*s = NullableString(*v)
|
|
} else {
|
|
*s = ""
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// StringInt is a JSON string representing an integer.
|
|
type StringInt int
|
|
|
|
func (i *StringInt) String() (s string) {
|
|
s = strconv.Itoa(int(*i))
|
|
if len(s) <= 4 {
|
|
s = strings.Repeat("0", 4-len(s)) + s
|
|
} else if len(s) < 6 {
|
|
s = strings.Repeat("0", 6-len(s)) + s
|
|
}
|
|
return
|
|
}
|
|
|
|
func (i *StringInt) MarshalJSON() ([]byte, error) { return json.Marshal(i.String()) }
|
|
func (i *StringInt) UnmarshalJSON(data []byte) (err error) {
|
|
var v string
|
|
err = json.Unmarshal(data, &v)
|
|
if err == nil {
|
|
var n int
|
|
n, err = strconv.Atoi(v)
|
|
if err == nil {
|
|
*i = StringInt(n)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// NewEncoder returns a new encoder that writes to w, configured to match upstream API behaviour.
|
|
func NewEncoder(w io.Writer) *json.Encoder {
|
|
e := json.NewEncoder(w)
|
|
e.SetEscapeHTML(false)
|
|
return e
|
|
}
|