package registry import ( "fmt" "iter" "maps" ) type Registry struct { codecs map[string]Codec converter *Converter engines map[string]Engine } func New() *Registry { return &Registry{ codecs: map[string]Codec{}, converter: NewConverter(), engines: map[string]Engine{}, } } func (r Registry) GetEngine(name string) (Engine, error) { e, ok := r.engines[name] if !ok { return nil, fmt.Errorf("engine '%s' not found", name) } return e, nil } func (r Registry) ListEngines() iter.Seq[Engine] { return maps.Values(r.engines) } func (r *Registry) GetDefaultEngine(id string) (Engine, error) { for _, engine := range r.engines { if engine.InType() == id { return engine, nil } } return nil, fmt.Errorf("no engine for '%s'", id) } func (r *Registry) ConvertTo(repr Repr, outType string) (Repr, error) { path, err := r.ConversionPath(repr.ID(), outType) if err != nil { return nil, err } result := repr for _, conversion := range path { result, err = conversion.Run(result) if err != nil { return nil, fmt.Errorf("converting '%s' to '%s': %w", conversion.InType(), conversion.OutType(), err) } } return result, err } func (r *Registry) Marshal(repr Repr) (string, error) { m, ok := r.codecs[repr.ID()] if !ok { return "", fmt.Errorf("no marshaler for '%s'", repr.ID()) } return m.Encode(repr) } func (r *Registry) Unmarshal(s string, outType string) (Repr, error) { m, ok := r.codecs[outType] if !ok { return nil, fmt.Errorf("no marshaler for '%s'", outType) } return m.Decode(s) } func reverse[T any](list []T) []T { if list == nil { return list } reversed := []T{} for i := len(list) - 1; i >= 0; i-- { reversed = append(reversed, list[i]) } return reversed } func (r *Registry) ConversionPath(from, to string) ([]Conversion, error) { backtrack := map[string]Conversion{} iteration := []string{from} for len(iteration) > 0 { nextIteration := []string{} for _, item := range iteration { for _, conversion := range r.converter.ConversionsFrom(item) { if _, ok := backtrack[conversion.OutType()]; ok { continue } nextIteration = append(nextIteration, conversion.OutType()) backtrack[conversion.OutType()] = conversion } } iteration = nextIteration } reversedPath := []Conversion{} current := to for current != from { conversion, ok := backtrack[current] if !ok { return nil, fmt.Errorf("no valid conversion from '%s' to '%s'", from, to) } reversedPath = append(reversedPath, conversion) current = conversion.InType() } return reverse(reversedPath), nil }