// Package models provides data models for the Yandex Music API. // All models implement the Model interface with DeJSON/DeList deserialization methods. package models import ( "encoding/json" "fmt" "strconv" "gitea.mrixs.me/Mrixs/yandex-music-api-go/internal/utils" ) // Model is the base interface for all models. // All model types implement this interface for JSON deserialization and serialization. type Model interface { // DeJSON deserializes a model from JSON data. DeJSON(data []byte, c ClientInterface) (Model, error) // DeList deserializes a list of models from JSON data. DeList(data []byte, c ClientInterface) ([]Model, error) // ToJSON serializes the model to JSON. ToJSON() ([]byte, error) // ToMap converts the model to a map. ToMap() (map[string]any, error) } // BaseModel provides common functionality for models, including a client reference. type BaseModel struct { Client ClientInterface `json:"-"` } // NewBaseModel creates a new BaseModel with the given client interface. func NewBaseModel(c ClientInterface) *BaseModel { return &BaseModel{Client: c} } // DeJSON implements Model.DeJSON for basic models. // This should be overridden by concrete types. func (b *BaseModel) DeJSON(data []byte, c ClientInterface) (Model, error) { // This should be overridden by concrete types return nil, nil } // DeList implements Model.DeList for basic models. // This should be overridden by concrete types. func (b *BaseModel) DeList(data []byte, c ClientInterface) ([]Model, error) { var rawList []json.RawMessage if err := json.Unmarshal(data, &rawList); err != nil { return nil, err } result := make([]Model, 0, len(rawList)) for range rawList { // This should be overridden by concrete types result = append(result, nil) } return result, nil } // ToJSON serializes the model to JSON using the default JSON backend. func (b *BaseModel) ToJSON() ([]byte, error) { return utils.Marshal(b) } // ToMap converts the model to a map by serializing to JSON and unmarshaling into a map. func (b *BaseModel) ToMap() (map[string]any, error) { data, err := b.ToJSON() if err != nil { return nil, err } var result map[string]any if err := json.Unmarshal(data, &result); err != nil { return nil, err } return result, nil } // SetClient sets the client interface on the model. func (b *BaseModel) SetClient(c ClientInterface) { b.Client = c } // GetClient returns the client interface associated with the model. func (b *BaseModel) GetClient() ClientInterface { return b.Client } // JSONID is an ID that can be represented as either a JSON string or a JSON number. // The Yandex Music API sometimes returns IDs as numbers (e.g., 12345) and sometimes // as strings (e.g., "12345"). JSONID accepts both and stores the value as a string. type JSONID string // UnmarshalJSON implements json.Unmarshaler, accepting both JSON strings and numbers. func (id *JSONID) UnmarshalJSON(data []byte) error { // Try string first var s string if err := json.Unmarshal(data, &s); err == nil { *id = JSONID(s) return nil } // Try number (float64 is the default JSON number decoding) var f float64 if err := json.Unmarshal(data, &f); err != nil { return fmt.Errorf("cannot unmarshal %v into JSONID: %w", string(data), err) } *id = JSONID(strconv.FormatFloat(f, 'f', -1, 64)) return nil } // String returns the ID as a Go string. func (id JSONID) String() string { return string(id) }