moved extractor/emitter code to the fan pkg
Some checks are pending
Lint / golangci-lint (push) Waiting to run
Declarative Tests / test (push) Waiting to run
Declarative Tests / build-fedora (push) Waiting to run
Declarative Tests / build-ubuntu-focal (push) Waiting to run

This commit is contained in:
Matthew Rich 2024-09-24 19:15:47 +00:00
parent 4eec426853
commit 38f8831275
14 changed files with 69 additions and 845 deletions

View File

@ -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
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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()

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}