// Copyright 2024 Matthew Rich . All rights reserved. package target import ( _ "context" _ "encoding/json" _ "fmt" _ "gopkg.in/yaml.v3" "net/url" "path/filepath" "decl/internal/resource" "os" "compress/gzip" "io" _ "errors" "log/slog" ) const ( FormatYaml = "yaml" FormatJson = "json" ) type DeclFile struct { Path string `yaml:"path" json:"path"` Gzip bool `yaml:"gzip,omitempty" json:"gzip,omitempty"` Format string `yaml:"format,omitempty" json:"format,omitempty"` encoder resource.Encoder `yaml:"-" json:"-"` closer func() error `yaml:"-" json:"-"` } func NewDeclFile() *DeclFile { return &DeclFile{ Gzip: false, closer: func() error { return nil } } } func NewFileDocTarget(u *url.URL, format string, gzip bool, fileUri bool) DocTarget { t := NewDeclFile() t.Format = format t.Gzip = gzip if fileUri { fileAbsolutePath, _ := filepath.Abs(filepath.Join(u.Hostname(), u.RequestURI())) t.Path = fileAbsolutePath } else { t.Path = filepath.Join(u.Hostname(), u.Path) } t.Open() return t } func init() { TargetTypes.Register([]string{"decl", "file"}, func(u *url.URL) DocTarget { t := NewDeclFile() if u.Path != "-" { t.Path,_ = filepath.Abs(filepath.Join(u.Hostname(), u.Path)) } else { t.Path = "-" } if _,ok := u.Query()["gzip"]; ok { t.Gzip = true } if format,ok := u.Query()["format"]; ok { switch format[0] { case string(FormatYaml): t.Format = FormatYaml case string(FormatJson): t.Format = FormatJson } } t.Open() return t }) TargetTypes.Register([]string{"yaml.gz","yml.gz"}, func(u *url.URL) DocTarget { switch u.Scheme { case "yaml", "yml", "file": return NewFileDocTarget(u, FormatYaml, true, false) } return NewFileDocTarget(u, FormatYaml, true, false) }) TargetTypes.Register([]string{"json.gz"}, func(u *url.URL) DocTarget { switch u.Scheme { case "json", "file": return NewFileDocTarget(u, FormatJson, true, false) } return NewFileDocTarget(u, FormatJson, true, false) }) TargetTypes.Register([]string{"yaml","yml"}, func(u *url.URL) DocTarget { switch u.Scheme { case "yaml", "yml", "file": return NewFileDocTarget(u, FormatYaml, false, false) } return NewFileDocTarget(u, FormatYaml, false, false) }) TargetTypes.Register([]string{"json"}, func(u *url.URL) DocTarget { switch u.Scheme { case "json", "file": return NewFileDocTarget(u, FormatJson, false, false) } return NewFileDocTarget(u, FormatJson, false, false) }) } func (d *DeclFile) Open() error { var file *os.File var fileErr error var fileWriter io.WriteCloser if d.Path == "" || d.Path == "-" { file = os.Stdout } else { file, fileErr = os.Open(d.Path) if fileErr != nil { return fileErr } d.closer = func() error { d.encoder.Close() fileWriter.Close() if file != fileWriter { file.Close() } return nil } } if d.Gzip { fileWriter = gzip.NewWriter(file) } else { fileWriter = file } switch d.Format { case FormatJson: d.encoder = resource.NewJSONEncoder(fileWriter) case FormatYaml: fallthrough default: d.encoder = resource.NewYAMLEncoder(fileWriter) } return nil } func (d *DeclFile) Close() error { return d.closer() } func (d *DeclFile) Type() string { return "decl" } func (d *DeclFile) EmitResources(documents []*resource.Document, filter resource.ResourceSelector) (error) { for _, doc := range documents { emitDoc := resource.NewDocument() if validationErr := doc.Validate(); validationErr != nil { return validationErr } for _, declaration := range doc.Filter(filter) { emitDoc.ResourceDecls = append(emitDoc.ResourceDecls, *declaration) } slog.Info("EmitResources", "doctarget", d, "encoder", d.encoder, "emit", emitDoc) if documentErr := d.encoder.Encode(emitDoc); documentErr != nil { slog.Info("EmitResources", "err", documentErr) return documentErr } } return nil }