package agent import ( "encoding/json" "forge.cadoles.com/Cadoles/emissary/internal/spec" "github.com/mitchellh/mapstructure" "github.com/pkg/errors" ) var ErrSpecNotFound = errors.New("spec not found") type Specs map[spec.Name]spec.Spec type State struct { specs Specs `json:"specs"` } func NewState() *State { return &State{ specs: make(map[spec.Name]spec.Spec), } } func (s *State) MarshalJSON() ([]byte, error) { state := struct { Specs map[spec.Name]*spec.RawSpec `json:"specs"` }{ Specs: func(specs map[spec.Name]spec.Spec) map[spec.Name]*spec.RawSpec { rawSpecs := make(map[spec.Name]*spec.RawSpec) for name, sp := range specs { rawSpecs[name] = &spec.RawSpec{ Name: sp.SpecName(), Revision: sp.SpecRevision(), Data: sp.SpecData(), } } return rawSpecs }(s.specs), } data, err := json.Marshal(state) if err != nil { return nil, errors.WithStack(err) } return data, nil } func (s *State) UnmarshalJSON(data []byte) error { state := struct { Specs map[spec.Name]*spec.RawSpec `json:"specs"` }{} if err := json.Unmarshal(data, &state); err != nil { return errors.WithStack(err) } s.specs = func(rawSpecs map[spec.Name]*spec.RawSpec) Specs { specs := make(Specs) for name, raw := range rawSpecs { specs[name] = spec.Spec(raw) } return specs }(state.Specs) return nil } func (s *State) Specs() Specs { return s.specs } func (s *State) ClearSpecs() *State { s.specs = make(map[spec.Name]spec.Spec) return s } func (s *State) SetSpec(sp spec.Spec) *State { if s.specs == nil { s.specs = make(map[spec.Name]spec.Spec) } s.specs[sp.SpecName()] = sp return s } func (s *State) GetSpec(name spec.Name, dest any) error { spec, exists := s.specs[name] if !exists { return errors.WithStack(ErrSpecNotFound) } if err := mapstructure.Decode(spec, dest); err != nil { return errors.WithStack(err) } if err := mapstructure.Decode(spec.SpecData(), dest); err != nil { return errors.WithStack(err) } return nil }