2022-04-14 13:54:35 -05:00
|
|
|
package coremodel
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
"github.com/grafana/thema"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
// ErrModelAlreadyRegistered is returned when trying to register duplicate model to Registry.
|
|
|
|
ErrModelAlreadyRegistered = errors.New("error registering duplicate model")
|
|
|
|
)
|
|
|
|
|
|
|
|
// Registry is a registry of coremodel instances.
|
|
|
|
type Registry struct {
|
|
|
|
lock sync.RWMutex
|
|
|
|
models []Interface
|
|
|
|
modelIdx map[string]Interface
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewRegistry returns a new Registry with the provided coremodel instances.
|
|
|
|
func NewRegistry(models ...Interface) (*Registry, error) {
|
|
|
|
r := &Registry{
|
|
|
|
models: make([]Interface, 0, len(models)),
|
|
|
|
modelIdx: make(map[string]Interface, len(models)),
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := r.addModels(models); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return r, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Register adds coremodels to the Registry.
|
|
|
|
func (r *Registry) Register(models ...Interface) error {
|
|
|
|
return r.addModels(models)
|
|
|
|
}
|
|
|
|
|
|
|
|
// List returns all coremodels registered in this Registry.
|
|
|
|
func (r *Registry) List() []Interface {
|
|
|
|
r.lock.RLock()
|
|
|
|
defer r.lock.RUnlock()
|
|
|
|
|
|
|
|
return r.models
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *Registry) addModels(models []Interface) error {
|
|
|
|
r.lock.Lock()
|
|
|
|
defer r.lock.Unlock()
|
|
|
|
|
|
|
|
// Update model index and return an error if trying to register a duplicate.
|
|
|
|
for _, m := range models {
|
|
|
|
k := m.Lineage().Name()
|
|
|
|
|
|
|
|
// Ensure assignability first. TODO will this blow up for dashboards?
|
|
|
|
if err := thema.AssignableTo(m.CurrentSchema(), m.GoType()); err != nil {
|
|
|
|
return fmt.Errorf("%s schema version %v not assignable to provided Go type: %w", k, m.CurrentSchema().Version(), err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, ok := r.modelIdx[k]; ok {
|
|
|
|
return ErrModelAlreadyRegistered
|
|
|
|
}
|
|
|
|
|
|
|
|
r.modelIdx[k] = m
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remake model list.
|
|
|
|
// TODO: this can be more performant (proper resizing, maybe single loop with index building, etc.).
|
|
|
|
r.models = r.models[:0]
|
|
|
|
for _, m := range r.modelIdx {
|
|
|
|
r.models = append(r.models, m)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2022-05-21 19:44:12 -05:00
|
|
|
|
|
|
|
// Get retrieves a coremodel with the given string identifier. nil, false
|
|
|
|
// is returned if no such coremodel exists.
|
|
|
|
func (r *Registry) Get(name string) (cm Interface, has bool) {
|
|
|
|
r.lock.RLock()
|
|
|
|
cm, has = r.modelIdx[name]
|
|
|
|
r.lock.RUnlock()
|
|
|
|
return
|
|
|
|
}
|