blasphem/pkg/config/config.go
2022-12-19 19:24:01 -05:00

82 lines
1.6 KiB
Go

package config
import (
"errors"
"fmt"
"reflect"
"strings"
server "dynatron.me/x/blasphem/pkg/server/config"
"gopkg.in/yaml.v3"
)
type Configured interface {
Conf() *Config
}
type Config struct {
DataDir *string `yaml:"data_dir,omitempty"`
Server *server.Config `yaml:"server"`
}
const (
IncludeTag = "!include"
IncludeDirNamedTag = "!include_dir_named"
IncludeDirListTag = "!include_dir_list"
IncludeDirMergeNamedTag = "!include_dir_merge_named"
SecretTag = "!secret"
)
func (c *Config) UnmarshalYAML(y *yaml.Node) error {
if y.Kind != yaml.MappingNode {
return yerr(y, errors.New("config root must be a map"))
}
cV := reflect.ValueOf(c).Elem()
components := make(map[string]reflect.Value, cV.NumField())
for i := 0; i < cV.NumField(); i++ {
if tag, ok := cV.Type().Field(i).Tag.Lookup("yaml"); ok {
tags := strings.Split(tag, ",")
components[tags[0]] = cV.Field(i)
}
}
for i := 0; i < len(y.Content); i += 2 {
k, v := y.Content[i], y.Content[i+1]
switch v.LongTag() {
case IncludeTag:
fallthrough
case IncludeDirNamedTag:
fallthrough
case IncludeDirMergeNamedTag:
fallthrough
case SecretTag:
panic(fmt.Errorf("tag %s not implemented", v.LongTag()))
default:
var key string
err := k.Decode(&key)
if err != nil {
return yerr(y, err)
}
if comp, ok := components[key]; ok {
val := reflect.New(comp.Type())
err := v.Decode(val.Interface())
if err != nil {
return yerr(y, err)
}
comp.Set(val.Elem())
}
}
}
return nil
}
func yerr(y *yaml.Node, ye error) error {
return fmt.Errorf("line %d: %w", y.Line, ye)
}