moved extractor/emitter code to the fan pkg
This commit is contained in:
parent
4eec426853
commit
38f8831275
@ -1,134 +0,0 @@
|
||||
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"gopkg.in/yaml.v3"
|
||||
"log/slog"
|
||||
"decl/internal/codec"
|
||||
)
|
||||
|
||||
type BlockType struct {
|
||||
Name string `json:"name" yaml:"name"`
|
||||
Type TypeName `json:"type" yaml:"type"`
|
||||
}
|
||||
|
||||
type Block struct {
|
||||
Name string `json:"name" yaml:"name"`
|
||||
Type TypeName `json:"type" yaml:"type"`
|
||||
Values Configuration `json:"values" yaml:"values"`
|
||||
}
|
||||
|
||||
func NewBlock() *Block {
|
||||
return &Block{ Type: "generic" }
|
||||
}
|
||||
|
||||
func (b *Block) Clone() *Block {
|
||||
return &Block {
|
||||
Type: b.Type,
|
||||
Values: b.Values.Clone(),
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Block) Load(r io.Reader) error {
|
||||
return codec.NewYAMLDecoder(r).Decode(b)
|
||||
}
|
||||
|
||||
func (b *Block) LoadBlock(yamlBlock string) (err error) {
|
||||
err = codec.NewYAMLStringDecoder(yamlBlock).Decode(b)
|
||||
slog.Info("LoadBlock()", "yaml", yamlBlock, "object", b, "err", err)
|
||||
return
|
||||
}
|
||||
|
||||
func (b *Block) NewConfiguration() error {
|
||||
uri := fmt.Sprintf("%s://", b.Type)
|
||||
newConfig, err := ConfigTypes.New(uri)
|
||||
b.Values = newConfig
|
||||
return err
|
||||
}
|
||||
|
||||
func (b *Block) GetValue(key string) (any, error) {
|
||||
return b.Values.GetValue(key)
|
||||
}
|
||||
|
||||
func (b *Block) Configuration() Configuration {
|
||||
return b.Values
|
||||
}
|
||||
|
||||
func (b *Block) SetURI(uri string) (e error) {
|
||||
b.Values = NewConfiguration(uri)
|
||||
if b.Values == nil {
|
||||
return ErrUnknownConfigurationType
|
||||
}
|
||||
b.Type = TypeName(b.Values.Type())
|
||||
_,e = b.Values.Read(context.Background())
|
||||
return e
|
||||
}
|
||||
|
||||
|
||||
func (b *Block) UnmarshalValue(value *BlockType) error {
|
||||
b.Name = value.Name
|
||||
if value.Type == "" {
|
||||
b.Type = "generic"
|
||||
} else {
|
||||
b.Type = value.Type
|
||||
}
|
||||
|
||||
newConfig, configErr := ConfigTypes.New(fmt.Sprintf("%s://", b.Type))
|
||||
if configErr != nil {
|
||||
return configErr
|
||||
}
|
||||
b.Values = newConfig
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Block) UnmarshalYAML(value *yaml.Node) error {
|
||||
t := &BlockType{}
|
||||
if unmarshalConfigurationTypeErr := value.Decode(t); unmarshalConfigurationTypeErr != nil {
|
||||
return unmarshalConfigurationTypeErr
|
||||
}
|
||||
|
||||
if err := b.UnmarshalValue(t); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
configurationVals := struct {
|
||||
Values yaml.Node `json:"values"`
|
||||
}{}
|
||||
if unmarshalValuesErr := value.Decode(&configurationVals); unmarshalValuesErr != nil {
|
||||
return unmarshalValuesErr
|
||||
}
|
||||
if unmarshalConfigurationErr := configurationVals.Values.Decode(b.Values); unmarshalConfigurationErr != nil {
|
||||
return unmarshalConfigurationErr
|
||||
}
|
||||
_, readErr := b.Values.Read(context.Background())
|
||||
return readErr
|
||||
}
|
||||
|
||||
func (b *Block) UnmarshalJSON(data []byte) error {
|
||||
t := &BlockType{}
|
||||
if unmarshalConfigurationTypeErr := json.Unmarshal(data, t); unmarshalConfigurationTypeErr != nil {
|
||||
return unmarshalConfigurationTypeErr
|
||||
}
|
||||
|
||||
if err := b.UnmarshalValue(t); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
configurationVals := struct {
|
||||
Values Configuration `json:"values"`
|
||||
}{Values: b.Values}
|
||||
if unmarshalValuesErr := json.Unmarshal(data, &configurationVals); unmarshalValuesErr != nil {
|
||||
return unmarshalValuesErr
|
||||
}
|
||||
_, readErr := b.Values.Read(context.Background())
|
||||
return readErr
|
||||
}
|
||||
|
||||
func (b *Block) MarshalYAML() (any, error) {
|
||||
return b, nil
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
_ "fmt"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var TempDir string
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
var err error
|
||||
TempDir, err = os.MkdirTemp("", "testconfig")
|
||||
if err != nil || TempDir == "" {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
rc := m.Run()
|
||||
|
||||
os.RemoveAll(TempDir)
|
||||
os.Exit(rc)
|
||||
}
|
||||
|
||||
func TestNewBlock(t *testing.T) {
|
||||
configYaml := `
|
||||
name: "foo"
|
||||
values:
|
||||
http_user: "test"
|
||||
http_pass: "password"
|
||||
`
|
||||
docReader := strings.NewReader(configYaml)
|
||||
|
||||
block := NewBlock()
|
||||
assert.NotNil(t, block)
|
||||
assert.Nil(t, block.Load(docReader))
|
||||
assert.Equal(t, "foo", block.Name)
|
||||
val, err := block.GetValue("http_user")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "test", val)
|
||||
|
||||
missingVal, missingErr := block.GetValue("content")
|
||||
assert.ErrorIs(t, missingErr, ErrUnknownConfigurationKey)
|
||||
assert.Nil(t, missingVal)
|
||||
}
|
@ -5,15 +5,18 @@ package config
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"decl/internal/codec"
|
||||
"decl/internal/data"
|
||||
"decl/internal/folio"
|
||||
"encoding/json"
|
||||
"gopkg.in/yaml.v3"
|
||||
"crypto/x509"
|
||||
)
|
||||
|
||||
func init() {
|
||||
ConfigTypes.Register([]string{"certificate"}, func(u *url.URL) Configuration {
|
||||
folio.DocumentRegistry.ConfigurationTypes.Register([]string{"certificate"}, func(u *url.URL) data.Configuration {
|
||||
c := NewCertificate()
|
||||
return c
|
||||
})
|
||||
@ -26,6 +29,18 @@ func NewCertificate() *Certificate {
|
||||
return &c
|
||||
}
|
||||
|
||||
func (c *Certificate) URI() string {
|
||||
return fmt.Sprintf("%s://%s", c.Type(), "")
|
||||
}
|
||||
|
||||
func (c *Certificate) SetURI(uri string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Certificate) SetParsedURI(uri *url.URL) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Certificate) Read(ctx context.Context) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
@ -61,7 +76,7 @@ func (c *Certificate) UnmarshalYAML(value *yaml.Node) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Certificate) Clone() Configuration {
|
||||
func (c *Certificate) Clone() data.Configuration {
|
||||
jsonGeneric, _ := json.Marshal(c)
|
||||
clone := NewCertificate()
|
||||
if unmarshalErr := json.Unmarshal(jsonGeneric, &clone); unmarshalErr != nil {
|
||||
@ -77,7 +92,12 @@ func (c *Certificate) Type() string {
|
||||
func (c *Certificate) GetValue(name string) (result any, err error) {
|
||||
var ok bool
|
||||
if result, ok = (*c)[name]; !ok {
|
||||
err = ErrUnknownConfigurationKey
|
||||
err = data.ErrUnknownConfigurationKey
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Certificate) Has(key string) (ok bool) {
|
||||
_, ok = (*c)[key]
|
||||
return
|
||||
}
|
||||
|
@ -1,45 +0,0 @@
|
||||
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
_ "context"
|
||||
_ "encoding/json"
|
||||
_ "fmt"
|
||||
_ "gopkg.in/yaml.v3"
|
||||
_ "net/url"
|
||||
_ "regexp"
|
||||
_ "strings"
|
||||
_ "os"
|
||||
_ "io"
|
||||
_ "compress/gzip"
|
||||
_ "archive/tar"
|
||||
_ "errors"
|
||||
_ "path/filepath"
|
||||
_ "decl/internal/codec"
|
||||
"embed"
|
||||
)
|
||||
|
||||
type ConfigurationSelector func(b *Block) bool
|
||||
|
||||
type ConfigSource interface {
|
||||
Type() string
|
||||
|
||||
Extract(filter ConfigurationSelector) ([]*Document, error)
|
||||
}
|
||||
|
||||
func NewConfigSource(uri string) ConfigSource {
|
||||
s, e := ConfigSourceTypes.New(uri)
|
||||
if e == nil {
|
||||
return s
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//go:embed configs/*.yaml
|
||||
var configFiles embed.FS
|
||||
|
||||
func Configurations() ([]*Document, error) {
|
||||
fs := NewConfigFS(configFiles)
|
||||
return fs.Extract(nil)
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
_ "context"
|
||||
_ "encoding/json"
|
||||
_ "fmt"
|
||||
_ "gopkg.in/yaml.v3"
|
||||
_ "net/url"
|
||||
_ "regexp"
|
||||
_ "strings"
|
||||
_ "os"
|
||||
_ "io"
|
||||
)
|
||||
|
||||
type ConfigTarget interface {
|
||||
Type() string
|
||||
|
||||
EmitResources(documents []*Document, filter ConfigurationSelector) error
|
||||
Close() error
|
||||
}
|
||||
|
||||
func NewConfigTarget(uri string) ConfigTarget {
|
||||
s, e := ConfigTargetTypes.New(uri)
|
||||
if e == nil {
|
||||
return s
|
||||
}
|
||||
return nil
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
_ "net/url"
|
||||
_ "decl/internal/codec"
|
||||
_ "io"
|
||||
"decl/internal/types"
|
||||
"decl/internal/data"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrUnknownConfigurationType = errors.New("Unknown configuration type")
|
||||
ErrUnknownConfigurationKey = errors.New("Unknown configuration key")
|
||||
ConfigTypes *types.Types[Configuration] = types.New[Configuration]()
|
||||
ConfigSourceTypes *types.Types[ConfigSource] = types.New[ConfigSource]()
|
||||
ConfigTargetTypes *types.Types[ConfigTarget] = types.New[ConfigTarget]()
|
||||
)
|
||||
|
||||
type TypeName string //`json:"type"`
|
||||
|
||||
type Configuration interface {
|
||||
Type() string
|
||||
data.Reader
|
||||
GetValue(name string) (any, error)
|
||||
Clone() Configuration
|
||||
}
|
||||
|
||||
func NewConfiguration(uri string) Configuration {
|
||||
c, e := ConfigTypes.New(uri)
|
||||
if e == nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *TypeName) UnmarshalJSON(b []byte) error {
|
||||
ConfigTypeName := strings.Trim(string(b), "\"")
|
||||
if ConfigTypes.Has(ConfigTypeName) {
|
||||
*n = TypeName(ConfigTypeName)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("%w: %s", ErrUnknownConfigurationType, ConfigTypeName)
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
_ "context"
|
||||
_ "fmt"
|
||||
"github.com/stretchr/testify/assert"
|
||||
_ "log"
|
||||
_ "os"
|
||||
_ "path/filepath"
|
||||
_ "strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewConfiguration(t *testing.T) {
|
||||
configurationUri := "generic://"
|
||||
testConfig := NewConfiguration(configurationUri)
|
||||
assert.NotNil(t, testConfig)
|
||||
v, _ := testConfig.GetValue("foo")
|
||||
assert.Nil(t, v)
|
||||
}
|
@ -1,202 +0,0 @@
|
||||
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"gopkg.in/yaml.v3"
|
||||
"io"
|
||||
"log/slog"
|
||||
_ "net/url"
|
||||
"github.com/sters/yaml-diff/yamldiff"
|
||||
"strings"
|
||||
"decl/internal/codec"
|
||||
_ "context"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrConfigUndefinedName = errors.New("Config block is missing a defined name")
|
||||
)
|
||||
|
||||
type ConfigNamesMap[Value any] map[string]Value
|
||||
|
||||
type Document struct {
|
||||
names ConfigNamesMap[*Block]
|
||||
ConfigBlocks []Block `json:"configurations" yaml:"configurations"`
|
||||
}
|
||||
|
||||
func NewDocument() *Document {
|
||||
return &Document{ names: make(ConfigNamesMap[*Block]) }
|
||||
}
|
||||
|
||||
func (d *Document) Filter(filter ConfigurationSelector) []*Block {
|
||||
configurations := make([]*Block, 0, len(d.ConfigBlocks))
|
||||
for i := range d.ConfigBlocks {
|
||||
filterConfig := &d.ConfigBlocks[i]
|
||||
if filter == nil || filter(filterConfig) {
|
||||
configurations = append(configurations, &d.ConfigBlocks[i])
|
||||
}
|
||||
}
|
||||
return configurations
|
||||
}
|
||||
|
||||
func (d *Document) Clone() *Document {
|
||||
clone := NewDocument()
|
||||
clone.ConfigBlocks = make([]Block, len(d.ConfigBlocks))
|
||||
for i, res := range d.ConfigBlocks {
|
||||
clone.ConfigBlocks[i] = *res.Clone()
|
||||
}
|
||||
return clone
|
||||
}
|
||||
|
||||
func (d *Document) Load(r io.Reader) error {
|
||||
c := codec.NewYAMLDecoder(r)
|
||||
return c.Decode(d);
|
||||
}
|
||||
|
||||
func (d *Document) Validate() error {
|
||||
jsonDocument, jsonErr := d.JSON()
|
||||
if jsonErr == nil {
|
||||
s := NewSchema("document")
|
||||
err := s.Validate(string(jsonDocument))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Document) Configurations() []Block {
|
||||
return d.ConfigBlocks
|
||||
}
|
||||
|
||||
func (d *Document) Generate(w io.Writer) error {
|
||||
e := codec.NewYAMLEncoder(w)
|
||||
err := e.Encode(d);
|
||||
if err == nil {
|
||||
return e.Close()
|
||||
}
|
||||
e.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *Document) Append(doc *Document) {
|
||||
if doc != nil {
|
||||
for i := range doc.ConfigBlocks {
|
||||
slog.Info("Document.Append()", "doc", doc, "block", doc.ConfigBlocks[i], "targetdoc", d)
|
||||
d.AddConfigurationBlock(doc.ConfigBlocks[i].Name, doc.ConfigBlocks[i].Type, doc.ConfigBlocks[i].Values)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Document) AddConfigurationBlock(configurationName string, configurationType TypeName, configuration Configuration) {
|
||||
cfg := NewBlock()
|
||||
cfg.Name = configurationName
|
||||
cfg.Type = configurationType
|
||||
cfg.Values = configuration
|
||||
d.names[cfg.Name] = cfg
|
||||
d.ConfigBlocks = append(d.ConfigBlocks, *cfg)
|
||||
}
|
||||
|
||||
func (d *Document) AddConfiguration(uri string) error {
|
||||
cfg := NewBlock()
|
||||
if e := cfg.SetURI(uri); e != nil {
|
||||
return e
|
||||
}
|
||||
if cfg.Name == "" {
|
||||
return ErrConfigUndefinedName
|
||||
}
|
||||
d.names[cfg.Name] = cfg
|
||||
d.ConfigBlocks = append(d.ConfigBlocks, *cfg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Document) Has(name string) bool {
|
||||
_, ok := d.names[name]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (d *Document) Get(name string) *Block {
|
||||
return d.names[name]
|
||||
}
|
||||
|
||||
func (d *Document) JSON() ([]byte, error) {
|
||||
return json.Marshal(d)
|
||||
}
|
||||
|
||||
func (d *Document) YAML() ([]byte, error) {
|
||||
return yaml.Marshal(d)
|
||||
}
|
||||
|
||||
func (d *Document) IndexName() error {
|
||||
for _, b := range d.ConfigBlocks {
|
||||
d.names[b.Name] = &b
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Document) UnmarshalYAML(value *yaml.Node) error {
|
||||
documentBlocks := struct {
|
||||
ConfigBlocks *[]Block `json:"configurations" yaml:"configurations"`
|
||||
}{ ConfigBlocks: &d.ConfigBlocks }
|
||||
if unmarshalDocumentErr := value.Decode(documentBlocks); unmarshalDocumentErr != nil {
|
||||
return unmarshalDocumentErr
|
||||
}
|
||||
return d.IndexName()
|
||||
}
|
||||
|
||||
func (d *Document) UnmarshalJSON(data []byte) error {
|
||||
documentBlocks := struct {
|
||||
ConfigBlocks *[]Block `json:"configurations" yaml:"configurations"`
|
||||
}{ ConfigBlocks: &d.ConfigBlocks }
|
||||
if unmarshalDocumentErr := json.Unmarshal(data, &documentBlocks); unmarshalDocumentErr != nil {
|
||||
return unmarshalDocumentErr
|
||||
}
|
||||
return d.IndexName()
|
||||
}
|
||||
|
||||
func (d *Document) Diff(with *Document, output io.Writer) (returnOutput string, diffErr error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
returnOutput = ""
|
||||
diffErr = fmt.Errorf("%s", r)
|
||||
}
|
||||
}()
|
||||
slog.Info("Document.Diff()")
|
||||
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 _,docDiffResults := range yamldiff.Do(yamlDiff, withDiff, opts...) {
|
||||
slog.Info("Diff()", "diff", docDiffResults, "dump", docDiffResults.Dump())
|
||||
_,e := output.Write([]byte(docDiffResults.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
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewDocument(t *testing.T) {
|
||||
d := NewDocument()
|
||||
assert.NotNil(t, d)
|
||||
}
|
||||
|
||||
func TestDocumentLoader(t *testing.T) {
|
||||
document := `
|
||||
---
|
||||
configurations:
|
||||
- type: generic
|
||||
name: global
|
||||
values:
|
||||
install_dir: /opt/jx
|
||||
- name: system
|
||||
values:
|
||||
dist: ubuntu
|
||||
release: focal
|
||||
`
|
||||
d := NewDocument()
|
||||
assert.NotNil(t, d)
|
||||
|
||||
docReader := strings.NewReader(document)
|
||||
|
||||
e := d.Load(docReader)
|
||||
assert.Nil(t, e)
|
||||
|
||||
configurations := d.Configurations()
|
||||
assert.Equal(t, 2, len(configurations))
|
||||
|
||||
b := d.Get("system")
|
||||
assert.NotNil(t, b)
|
||||
cfg := b.Configuration()
|
||||
value, valueErr := cfg.GetValue("dist")
|
||||
assert.Nil(t, valueErr)
|
||||
assert.Equal(t, "ubuntu", value)
|
||||
}
|
||||
|
||||
func TestDocumentJSONSchema(t *testing.T) {
|
||||
document := NewDocument()
|
||||
document.ConfigBlocks = []Block{}
|
||||
e := document.Validate()
|
||||
assert.Nil(t, e)
|
||||
}
|
@ -5,15 +5,18 @@ package config
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"decl/internal/codec"
|
||||
"decl/internal/command"
|
||||
"decl/internal/data"
|
||||
"decl/internal/folio"
|
||||
"encoding/json"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func init() {
|
||||
ConfigTypes.Register([]string{"exec"}, func(u *url.URL) Configuration {
|
||||
folio.DocumentRegistry.ConfigurationTypes.Register([]string{"exec"}, func(u *url.URL) data.Configuration {
|
||||
x := NewExec()
|
||||
return x
|
||||
})
|
||||
@ -32,6 +35,18 @@ func NewExec() *Exec {
|
||||
return x
|
||||
}
|
||||
|
||||
func (x *Exec) SetURI(uri string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Exec) SetParsedURI(uri *url.URL) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Exec) URI() string {
|
||||
return fmt.Sprintf("%s://%s", x.Type(), x.Path)
|
||||
}
|
||||
|
||||
func (x *Exec) Read(ctx context.Context) ([]byte, error) {
|
||||
out, err := x.ReadCommand.Execute(x)
|
||||
if err != nil {
|
||||
@ -78,7 +93,7 @@ func (x *Exec) UnmarshalYAML(value *yaml.Node) error {
|
||||
}
|
||||
|
||||
|
||||
func (x *Exec) Clone() Configuration {
|
||||
func (x *Exec) Clone() data.Configuration {
|
||||
clone := NewExec()
|
||||
clone.Path = x.Path
|
||||
clone.Args = x.Args
|
||||
@ -95,11 +110,15 @@ func (x *Exec) Type() string {
|
||||
func (x *Exec) GetValue(name string) (result any, err error) {
|
||||
var ok bool
|
||||
if result, ok = x.Values[name]; !ok {
|
||||
err = ErrUnknownConfigurationKey
|
||||
err = data.ErrUnknownConfigurationKey
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (x *Exec) Has(key string) (ok bool) {
|
||||
_, ok = x.Values[key]
|
||||
return
|
||||
}
|
||||
|
||||
func (ex *Exec) NewReadConfigCommand() {
|
||||
ex.ReadCommand = command.NewCommand()
|
||||
|
@ -1,143 +0,0 @@
|
||||
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
_ "context"
|
||||
_ "encoding/json"
|
||||
_ "fmt"
|
||||
_ "gopkg.in/yaml.v3"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"decl/internal/transport"
|
||||
"decl/internal/codec"
|
||||
_ "os"
|
||||
"io"
|
||||
"errors"
|
||||
"log/slog"
|
||||
)
|
||||
|
||||
type ConfigFile struct {
|
||||
Path string `yaml:"path" json:"path"`
|
||||
Format codec.Format `yaml:"format" json:"format"`
|
||||
reader *transport.Reader `yaml:"-" json:"-"`
|
||||
writer *transport.Writer `yaml:"-" json:"-"`
|
||||
encoder codec.Encoder `yaml:"-" json:"-"`
|
||||
decoder codec.Decoder `yaml:"-" json:"-"`
|
||||
}
|
||||
|
||||
func NewConfigFile() *ConfigFile {
|
||||
return &ConfigFile{ Format: codec.FormatYaml }
|
||||
}
|
||||
|
||||
func NewConfigFileFromURI(u *url.URL) *ConfigFile {
|
||||
t := NewConfigFile()
|
||||
if u.Scheme == "file" {
|
||||
t.Path,_ = filepath.Abs(filepath.Join(u.Hostname(), u.Path))
|
||||
} else {
|
||||
t.Path = filepath.Join(u.Hostname(), u.Path)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func NewConfigFileSource(u *url.URL) *ConfigFile {
|
||||
t := NewConfigFileFromURI(u)
|
||||
t.reader,_ = transport.NewReader(u)
|
||||
contentType := codec.Format(t.reader.ContentType())
|
||||
if contentType.Validate() == nil {
|
||||
if formatErr := t.Format.Set(t.reader.ContentType()); formatErr != nil {
|
||||
panic(formatErr)
|
||||
}
|
||||
}
|
||||
t.decoder = codec.NewDecoder(t.reader, t.Format)
|
||||
return t
|
||||
}
|
||||
|
||||
func NewConfigFileTarget(u *url.URL) *ConfigFile {
|
||||
t := NewConfigFileFromURI(u)
|
||||
t.writer,_ = transport.NewWriter(u)
|
||||
contentType := codec.Format(t.writer.ContentType())
|
||||
if contentType.Validate() == nil {
|
||||
if formatErr := t.Format.Set(t.writer.ContentType()); formatErr != nil {
|
||||
panic(formatErr)
|
||||
}
|
||||
}
|
||||
t.encoder = codec.NewEncoder(t.writer, t.Format)
|
||||
return t
|
||||
}
|
||||
|
||||
func init() {
|
||||
ConfigSourceTypes.Register([]string{"file"}, func(u *url.URL) ConfigSource {
|
||||
return NewConfigFileSource(u)
|
||||
})
|
||||
|
||||
ConfigSourceTypes.Register([]string{"pb","pb.gz","json","json.gz","yaml","yml","yaml.gz","yml.gz"}, func(u *url.URL) ConfigSource {
|
||||
return NewConfigFileSource(u)
|
||||
})
|
||||
|
||||
ConfigTargetTypes.Register([]string{"file"}, func(u *url.URL) ConfigTarget {
|
||||
return NewConfigFileTarget(u)
|
||||
})
|
||||
|
||||
ConfigTargetTypes.Register([]string{"pb","pb.gz","json","json.gz","yaml","yml","yaml.gz","yml.gz"}, func(u *url.URL) ConfigTarget {
|
||||
return NewConfigFileTarget(u)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
func (c *ConfigFile) Type() string { return "file" }
|
||||
|
||||
func (c *ConfigFile) Extract(filter ConfigurationSelector) ([]*Document, error) {
|
||||
documents := make([]*Document, 0, 100)
|
||||
|
||||
defer func() {
|
||||
c.reader.Close()
|
||||
}()
|
||||
|
||||
slog.Info("Extract()", "documents", documents)
|
||||
index := 0
|
||||
for {
|
||||
doc := NewDocument()
|
||||
e := c.decoder.Decode(doc)
|
||||
if errors.Is(e, io.EOF) {
|
||||
break
|
||||
}
|
||||
if e != nil {
|
||||
return documents, e
|
||||
}
|
||||
slog.Info("Extract()", "res", doc.ConfigBlocks[0].Values)
|
||||
if validationErr := doc.Validate(); validationErr != nil {
|
||||
return documents, validationErr
|
||||
}
|
||||
documents = append(documents, doc)
|
||||
index++
|
||||
}
|
||||
return documents, nil
|
||||
}
|
||||
|
||||
func (c *ConfigFile) EmitResources(documents []*Document, filter ConfigurationSelector) (error) {
|
||||
defer func() {
|
||||
c.encoder.Close()
|
||||
c.writer.Close()
|
||||
}()
|
||||
|
||||
for _, doc := range documents {
|
||||
emitDoc := NewDocument()
|
||||
if validationErr := doc.Validate(); validationErr != nil {
|
||||
return validationErr
|
||||
}
|
||||
for _, block := range doc.Filter(filter) {
|
||||
emitDoc.ConfigBlocks = append(emitDoc.ConfigBlocks, *block)
|
||||
}
|
||||
slog.Info("EmitResources", "doctarget", c, "encoder", c.encoder, "emit", emitDoc)
|
||||
if documentErr := c.encoder.Encode(emitDoc); documentErr != nil {
|
||||
slog.Info("EmitResources", "err", documentErr)
|
||||
return documentErr
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ConfigFile) Close() (error) {
|
||||
return nil
|
||||
}
|
@ -1,109 +0,0 @@
|
||||
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
||||
|
||||
package config
|
||||
|
||||
import (
|
||||
_ "context"
|
||||
_ "encoding/json"
|
||||
_ "fmt"
|
||||
_ "gopkg.in/yaml.v3"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"decl/internal/codec"
|
||||
"os"
|
||||
"io"
|
||||
"errors"
|
||||
"io/fs"
|
||||
"log/slog"
|
||||
)
|
||||
|
||||
|
||||
type ConfigFS struct {
|
||||
Path string `yaml:"path" json:"path"`
|
||||
subDirsStack []fs.FS `yaml:"-" json:"-"`
|
||||
fsys fs.FS `yaml:"-" json:"-"`
|
||||
}
|
||||
|
||||
func NewConfigFS(fsys fs.FS) *ConfigFS {
|
||||
return &ConfigFS{
|
||||
subDirsStack: make([]fs.FS, 0, 100),
|
||||
fsys: fsys,
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
ConfigSourceTypes.Register([]string{"fs"}, func(u *url.URL) ConfigSource {
|
||||
|
||||
t := NewConfigFS(nil)
|
||||
t.Path,_ = filepath.Abs(filepath.Join(u.Hostname(), u.Path))
|
||||
t.fsys = os.DirFS(t.Path)
|
||||
return t
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func (c *ConfigFS) Type() string { return "fs" }
|
||||
|
||||
func (c *ConfigFS) ExtractDirectory(fsys fs.FS) ([]*Document, error) {
|
||||
documents := make([]*Document, 0, 100)
|
||||
|
||||
files, err := fs.ReadDir(fsys, ".")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _,file := range files {
|
||||
|
||||
slog.Info("ConfigFS.ExtractDirectory", "file", file)
|
||||
if file.IsDir() {
|
||||
dir, subErr := fs.Sub(fsys, file.Name())
|
||||
if subErr != nil {
|
||||
return nil, subErr
|
||||
}
|
||||
c.subDirsStack = append(c.subDirsStack, dir)
|
||||
} else {
|
||||
fileHandle, fileErr := fsys.Open(file.Name())
|
||||
if fileErr != nil {
|
||||
return nil, fileErr
|
||||
}
|
||||
decoder := codec.NewYAMLDecoder(fileHandle)
|
||||
doc := NewDocument()
|
||||
e := decoder.Decode(doc)
|
||||
if errors.Is(e, io.EOF) {
|
||||
break
|
||||
}
|
||||
if e != nil {
|
||||
return documents, e
|
||||
}
|
||||
slog.Info("ConfigFS.ExtractDirectory", "doc", doc)
|
||||
if validationErr := doc.Validate(); validationErr != nil {
|
||||
return documents, validationErr
|
||||
}
|
||||
documents = append(documents, doc)
|
||||
}
|
||||
}
|
||||
return documents, nil
|
||||
}
|
||||
|
||||
func (c *ConfigFS) Extract(filter ConfigurationSelector) ([]*Document, error) {
|
||||
documents := make([]*Document, 0, 100)
|
||||
|
||||
path := c.fsys
|
||||
c.subDirsStack = append(c.subDirsStack, path)
|
||||
|
||||
for {
|
||||
if len(c.subDirsStack) == 0 {
|
||||
break
|
||||
}
|
||||
var dirPath fs.FS
|
||||
dirPath, c.subDirsStack = c.subDirsStack[len(c.subDirsStack) - 1], c.subDirsStack[:len(c.subDirsStack) - 1]
|
||||
docs, dirErr := c.ExtractDirectory(dirPath)
|
||||
if dirErr != nil {
|
||||
return documents, dirErr
|
||||
}
|
||||
|
||||
documents = append(documents, docs...)
|
||||
}
|
||||
return documents, nil
|
||||
}
|
||||
|
@ -6,10 +6,13 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"fmt"
|
||||
"decl/internal/data"
|
||||
"decl/internal/folio"
|
||||
)
|
||||
|
||||
func init() {
|
||||
ConfigTypes.Register([]string{"generic"}, func(u *url.URL) Configuration {
|
||||
folio.DocumentRegistry.ConfigurationTypes.Register([]string{"generic"}, func(u *url.URL) data.Configuration {
|
||||
g := NewGeneric[any]()
|
||||
return g
|
||||
})
|
||||
@ -22,7 +25,19 @@ func NewGeneric[Value any]() *Generic[Value] {
|
||||
return &g
|
||||
}
|
||||
|
||||
func (g *Generic[Value]) Clone() Configuration {
|
||||
func (g *Generic[Value]) URI() string {
|
||||
return fmt.Sprintf("%s://%s", g.Type(), "")
|
||||
}
|
||||
|
||||
func (g *Generic[Value]) SetURI(uri string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Generic[Value]) SetParsedURI(uri *url.URL) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Generic[Value]) Clone() data.Configuration {
|
||||
jsonGeneric, _ := json.Marshal(g)
|
||||
clone := NewGeneric[Value]()
|
||||
if unmarshalErr := json.Unmarshal(jsonGeneric, clone); unmarshalErr != nil {
|
||||
@ -42,7 +57,12 @@ func (g *Generic[Value]) Read(context.Context) ([]byte, error) {
|
||||
func (g *Generic[Value]) GetValue(name string) (result any, err error) {
|
||||
var ok bool
|
||||
if result, ok = (*g)[name]; !ok {
|
||||
err = ErrUnknownConfigurationKey
|
||||
err = data.ErrUnknownConfigurationKey
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (g *Generic[Value]) Has(key string) (ok bool) {
|
||||
_, ok = (*g)[key]
|
||||
return
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ func (s *System) Read(context.Context) ([]byte, error) {
|
||||
func (s *System) GetValue(name string) (result any, err error) {
|
||||
var ok bool
|
||||
if result, ok = (*s)[name]; !ok {
|
||||
err = ErrUnknownConfigurationKey
|
||||
err = data.ErrUnknownConfigurationKey
|
||||
}
|
||||
return
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user