2024-08-18 01:19:56 +00:00
|
|
|
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
|
|
|
|
|
|
|
|
package folio
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
2024-09-19 07:57:26 +00:00
|
|
|
"net/url"
|
2024-08-18 01:19:56 +00:00
|
|
|
"strings"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
|
|
"decl/internal/codec"
|
|
|
|
"decl/internal/data"
|
|
|
|
"decl/internal/schema"
|
|
|
|
)
|
|
|
|
|
2024-10-16 17:26:42 +00:00
|
|
|
type ConfigKey string
|
|
|
|
|
|
|
|
// Lookup a config value using block.key identifier (E.g. system.GOOS)
|
|
|
|
func (c ConfigKey) GetValue() (value any, err error) {
|
|
|
|
fields := strings.SplitN(string(c), ".", 2)
|
|
|
|
if configBlock, ok := DocumentRegistry.ConfigNameMap.Get(fields[0]); ok && len(fields) > 1 {
|
|
|
|
return configBlock.GetValue(fields[1])
|
|
|
|
} else {
|
|
|
|
return nil, fmt.Errorf("%w - %s", data.ErrUnknownConfigurationType, c)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c ConfigKey) Get() any {
|
|
|
|
if v, err := c.GetValue(); err == nil {
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c ConfigKey) GetStringSlice() []string {
|
|
|
|
if v, err := c.GetValue(); err == nil {
|
|
|
|
return v.([]string)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2024-08-18 01:19:56 +00:00
|
|
|
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 data.Configuration `json:"values" yaml:"values"`
|
|
|
|
ConfigurationTypes data.TypesRegistry[data.Configuration] `json:"-" yaml:"-"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewBlock() *Block {
|
|
|
|
return &Block{ Type: "generic", ConfigurationTypes: DocumentRegistry.ConfigurationTypes }
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *Block) Clone() data.Block {
|
|
|
|
return &Block {
|
|
|
|
Type: b.Type,
|
|
|
|
Values: b.Values.Clone(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *Block) Load(docData []byte, f codec.Format) (err error) {
|
|
|
|
err = f.StringDecoder(string(docData)).Decode(b)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *Block) LoadReader(r io.ReadCloser, f codec.Format) (err error) {
|
|
|
|
err = f.Decoder(r).Decode(b)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *Block) LoadString(docData string, f codec.Format) (err error) {
|
|
|
|
err = f.StringDecoder(docData).Decode(b)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *Block) LoadBlock(yamlBlock string) (err error) {
|
|
|
|
return b.LoadString(yamlBlock, codec.FormatYaml)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *Block) JSON() ([]byte, error) {
|
|
|
|
var buf strings.Builder
|
|
|
|
err := codec.FormatJson.Serialize(b, &buf)
|
|
|
|
return []byte(buf.String()), err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *Block) YAML() ([]byte, error) {
|
|
|
|
var buf strings.Builder
|
|
|
|
err := codec.FormatYaml.Serialize(b, &buf)
|
|
|
|
return []byte(buf.String()), err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *Block) PB() ([]byte, error) {
|
|
|
|
var buf strings.Builder
|
|
|
|
err := codec.FormatProtoBuf.Serialize(b, &buf)
|
|
|
|
return []byte(buf.String()), err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *Block) Validate() (err error) {
|
|
|
|
var blockJson []byte
|
|
|
|
if blockJson, err = b.JSON(); err == nil {
|
|
|
|
s := schema.New(fmt.Sprintf("%s-block", b.Type), schemaFiles) // XXX wrong schemaFiles
|
|
|
|
err = s.Validate(string(blockJson))
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *Block) NewConfiguration(uri *string) (err error) {
|
|
|
|
if b.ConfigurationTypes == nil {
|
|
|
|
panic(fmt.Errorf("Undefined type registry: unable to create new configuration %s", *uri))
|
|
|
|
}
|
|
|
|
if uri == nil {
|
|
|
|
b.Values, err = b.ConfigurationTypes.New(fmt.Sprintf("%s://", b.Type))
|
|
|
|
} else {
|
|
|
|
b.Values, err = b.ConfigurationTypes.New(*uri)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-09-19 07:57:26 +00:00
|
|
|
func (b *Block) NewConfigurationFromParsedURI(uri *url.URL) (err error) {
|
|
|
|
if uri == nil {
|
|
|
|
b.Values, err = b.ConfigurationTypes.New(fmt.Sprintf("%s://", b.Type))
|
|
|
|
} else {
|
|
|
|
b.Values, err = b.ConfigurationTypes.NewFromParsedURI(uri)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-08-18 01:19:56 +00:00
|
|
|
func (b *Block) GetValue(key string) (any, error) {
|
|
|
|
return b.Values.GetValue(key)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *Block) Configuration() data.Configuration {
|
|
|
|
return b.Values
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *Block) ConfigurationType() data.TypeName {
|
|
|
|
return data.TypeName(b.Type)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *Block) URI() string {
|
|
|
|
return b.Values.URI()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *Block) SetURI(uri string) (err error) {
|
|
|
|
if b.Values == nil {
|
|
|
|
if err = b.NewConfiguration(&uri); err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if b.Values == nil {
|
|
|
|
return fmt.Errorf("%w - %s", data.ErrUnknownConfigurationType, uri)
|
|
|
|
}
|
|
|
|
b.Type = TypeName(b.Values.Type())
|
|
|
|
_,err = b.Values.Read(context.Background())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-10-16 17:26:42 +00:00
|
|
|
func (b *Block) SetParsedURI(uri data.URIParser) (err error) {
|
2024-09-19 07:57:26 +00:00
|
|
|
if b.Values == nil {
|
2024-10-16 17:26:42 +00:00
|
|
|
if err = b.NewConfigurationFromParsedURI(uri.URL()); err != nil {
|
2024-09-19 07:57:26 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if b.Values == nil {
|
|
|
|
return fmt.Errorf("%w - %s", data.ErrUnknownConfigurationType, uri)
|
|
|
|
}
|
|
|
|
b.Type = TypeName(b.Values.Type())
|
|
|
|
_,err = b.Values.Read(context.Background())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-08-18 01:19:56 +00:00
|
|
|
func (b *Block) UnmarshalValue(value *BlockType) error {
|
|
|
|
if b.ConfigurationTypes == nil {
|
|
|
|
panic(fmt.Errorf("Undefined type registry: unable to create new configuration %s", value.Type))
|
|
|
|
}
|
|
|
|
b.Name = value.Name
|
|
|
|
if value.Type == "" {
|
|
|
|
b.Type = "generic"
|
|
|
|
} else {
|
|
|
|
b.Type = value.Type
|
|
|
|
}
|
|
|
|
|
|
|
|
newConfig, configErr := b.ConfigurationTypes.New(fmt.Sprintf("%s://", b.Type))
|
|
|
|
if configErr != nil {
|
|
|
|
return configErr
|
|
|
|
}
|
|
|
|
b.Values = newConfig
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *Block) UnmarshalYAML(value *yaml.Node) error {
|
2024-09-19 07:57:26 +00:00
|
|
|
if b.ConfigurationTypes == nil {
|
|
|
|
b.ConfigurationTypes = DocumentRegistry.ConfigurationTypes
|
|
|
|
}
|
2024-08-18 01:19:56 +00:00
|
|
|
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(jsonData []byte) error {
|
2024-09-19 07:57:26 +00:00
|
|
|
if b.ConfigurationTypes == nil {
|
|
|
|
b.ConfigurationTypes = DocumentRegistry.ConfigurationTypes
|
|
|
|
}
|
2024-08-18 01:19:56 +00:00
|
|
|
t := &BlockType{}
|
|
|
|
if unmarshalConfigurationTypeErr := json.Unmarshal(jsonData, t); unmarshalConfigurationTypeErr != nil {
|
|
|
|
return unmarshalConfigurationTypeErr
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := b.UnmarshalValue(t); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
configurationVals := struct {
|
|
|
|
Values data.Configuration `json:"values"`
|
|
|
|
}{Values: b.Values}
|
|
|
|
if unmarshalValuesErr := json.Unmarshal(jsonData, &configurationVals); unmarshalValuesErr != nil {
|
|
|
|
return unmarshalValuesErr
|
|
|
|
}
|
|
|
|
_, readErr := b.Values.Read(context.Background())
|
|
|
|
return readErr
|
|
|
|
}
|