// Copyright 2024 Matthew Rich . All rights reserved. package folio import ( "context" "encoding/json" "fmt" "io" "net/url" "strings" "gopkg.in/yaml.v3" "decl/internal/codec" "decl/internal/data" "decl/internal/schema" ) 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 } 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 } 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 } 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 } func (b *Block) SetParsedURI(uri data.URIParser) (err error) { if b.Values == nil { if err = b.NewConfigurationFromParsedURI(uri.URL()); 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 } 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 { if b.ConfigurationTypes == nil { b.ConfigurationTypes = DocumentRegistry.ConfigurationTypes } 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 { if b.ConfigurationTypes == nil { b.ConfigurationTypes = DocumentRegistry.ConfigurationTypes } 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 }