package registry import ( "fmt" "git.maximhutz.com/max/lambda/internal/cli" ) type Registry struct { marshalers map[string]cli.Marshaler converter *Converter engines map[string]cli.Engine } func New() *Registry { return &Registry{ marshalers: map[string]cli.Marshaler{}, converter: NewConverter(), engines: map[string]cli.Engine{}, } } func (r *Registry) AddConversions(conversions ...cli.Conversion) error { for _, conversion := range conversions { r.converter.Add(conversion) } return nil } func (r *Registry) AddMarshaler(c cli.Marshaler) error { if _, ok := r.marshalers[c.InType()]; ok { return fmt.Errorf("marshaler for '%s' already registered", c.InType()) } r.marshalers[c.InType()] = c return nil } func (r *Registry) AddEngine(e cli.Engine) error { if _, ok := r.engines[e.Name()]; ok { return fmt.Errorf("engine '%s' already registered", e.Name()) } r.engines[e.Name()] = e return nil } func (r *Registry) GetEngine(name string) (cli.Engine, error) { e, ok := r.engines[name] if !ok { return nil, fmt.Errorf("engine '%s' not found", name) } return e, nil } func (r *Registry) GetDefaultEngine(id string) (cli.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 cli.Repr, outType string) (cli.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 cli.Repr) (string, error) { m, ok := r.marshalers[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) (cli.Repr, error) { m, ok := r.marshalers[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) ([]cli.Conversion, error) { backtrack := map[string]cli.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 := []cli.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 }