// Copyright 2024 Matthew Rich . All rights reserved. package resource import ( "encoding/json" "fmt" "gopkg.in/yaml.v3" "io" "log/slog" _ "net/url" "github.com/sters/yaml-diff/yamldiff" "strings" ) type Document struct { ResourceDecls []Declaration `json:"resources" yaml:"resources"` } func NewDocument() *Document { return &Document{} } func (d *Document) Filter(filter ResourceSelector) []*Declaration { resources := make([]*Declaration, 0, len(d.ResourceDecls)) for i := range d.ResourceDecls { filterResource := &d.ResourceDecls[i] if filter(filterResource) { resources = append(resources, &d.ResourceDecls[i]) } } return resources } func (d *Document) Clone() *Document { clone := NewDocument() clone.ResourceDecls = make([]Declaration, len(d.ResourceDecls)) for i, res := range d.ResourceDecls { clone.ResourceDecls[i] = *res.Clone() } return clone } func (d *Document) Load(r io.Reader) error { c := NewYAMLDecoder(r) return c.Decode(d); } func (d *Document) Validate() error { jsonDocument, jsonErr := d.JSON() slog.Info("document.Validate() json", "json", jsonDocument, "err", jsonErr) if jsonErr == nil { s := NewSchema("document") err := s.Validate(string(jsonDocument)) if err != nil { return err } /* for i := range d.ResourceDecls { if e := d.ResourceDecls[i].Resource().Validate(); e != nil { return fmt.Errorf("failed to validate resource %s; %w", d.ResourceDecls[i].Resource().URI(), e) } } */ } return nil } func (d *Document) Resources() []Declaration { return d.ResourceDecls } func (d *Document) Apply() error { if d == nil { panic("Undefined Document") } slog.Info("Document.Apply()", "declarations", d) for i := range d.ResourceDecls { if e := d.ResourceDecls[i].Resource().Apply(); e != nil { return e } } return nil } func (d *Document) Generate(w io.Writer) error { e := NewYAMLEncoder(w) err := e.Encode(d); if err == nil { return e.Close() } e.Close() return err } func (d *Document) AddResourceDeclaration(resourceType string, resourceDeclaration Resource) { decl := NewDeclaration() decl.Type = TypeName(resourceType) decl.Attributes = resourceDeclaration d.ResourceDecls = append(d.ResourceDecls, *decl) } func (d *Document) AddResource(uri string) error { //parsedResourceURI, e := url.Parse(uri) //if e == nil { decl := NewDeclaration() if e := decl.SetURI(uri); e != nil { return e } d.ResourceDecls = append(d.ResourceDecls, *decl) //} return nil } func (d *Document) JSON() ([]byte, error) { return json.Marshal(d) } func (d *Document) YAML() ([]byte, error) { return yaml.Marshal(d) } func (d *Document) Diff(with *Document, output io.Writer) (string, error) { opts := []yamldiff.DoOptionFunc{} if output == nil { output = &strings.Builder{} } ydata, yerr := d.YAML() if yerr != nil { return "", yerr } yamlDiff,yamlDiffErr := yamldiff.Load(string(ydata)) if yamlDiffErr != nil { return "", yamlDiffErr } wdata,werr := with.YAML() if werr != nil { return "", werr } withDiff,withDiffErr := yamldiff.Load(string(wdata)) if withDiffErr != nil { return "", withDiffErr } for _,diff := range yamldiff.Do(yamlDiff, withDiff, opts...) { slog.Info("Diff()", "diff", diff) fmt.Printf("yaml %#v with %#v\n", yamlDiff, withDiff) _,e := output.Write([]byte(diff.Dump())) if e != nil { return "", e } } slog.Info("Document.Diff() ", "document.yaml", ydata, "with.yaml", wdata) if stringOutput, ok := output.(*strings.Builder); ok { return stringOutput.String(), nil } return "", nil }