Something went wrong on our end
-
Volker Schukai authoredVolker Schukai authored
import.go 3.93 KiB
// Copyright 2022 schukai GmbH
// SPDX-License-Identifier: AGPL-3.0
package configuration
import (
"bytes"
"encoding/json"
"fmt"
"github.com/imdario/mergo"
"github.com/magiconair/properties"
toml "github.com/pelletier/go-toml/v2"
"gitlab.schukai.com/oss/libraries/go/utilities/pathfinder.git"
yaml "gopkg.in/yaml.v3"
"io"
"os"
"path"
"reflect"
)
func importJson[C any](config *C, reader io.Reader) error {
decoder := json.NewDecoder(reader)
return decoder.Decode(config)
}
func importYaml[C any](config *C, reader io.Reader) error {
decoder := yaml.NewDecoder(reader)
return decoder.Decode(config)
}
func importToml[C any](config *C, reader io.Reader) error {
decoder := toml.NewDecoder(reader)
return decoder.Decode(config)
}
func importProperties[C any](config *C, reader io.Reader) error {
buf := &bytes.Buffer{}
_, err := buf.ReadFrom(reader)
if err != nil {
return err
}
b := buf.Bytes()
p, err := properties.Load(b, properties.UTF8)
if err != nil {
return err
}
return p.Decode(config)
}
func (s *Settings[C]) importStream(r reader, f ...func(n *C)) {
var c C
var err error
defer func() {
s.notifyErrorHooks()
}()
x := r.reader
switch r.format {
case Json:
err = importJson(&c, x)
case Yaml:
err = importYaml(&c, x)
case Toml:
err = importToml(&c, x)
case Properties:
err = importProperties(&c, x)
default:
err = newFormatNotSupportedError(r.format)
}
if err == nil {
s.importCounter++
} else {
s.errors = append(s.errors, err)
}
for _, fn := range f { // https://staticcheck.dev/docs/checks/#S1031
fn(&c)
}
if err := mergo.Merge(&s.config, c); err != nil {
s.errors = append(s.errors, err)
}
}
func (s *Settings[C]) importStreams() {
for _, r := range s.stream.readers {
s.importStream(r)
}
}
var typeOfPathValue PathValue
// replacePath replaces all relative paths with absolute paths
func replacePath[C any](basePath string, c C) error {
paths := []string{}
top := reflect.TypeOf(typeOfPathValue)
toc := reflect.ValueOf(c)
ptp := &paths
pathfinder.FindPaths(toc, top, []string{}, ptp)
for _, px := range paths {
p, err := pathfinder.GetValue[C](c, px)
if err != nil {
return err
}
switch p.(type) {
case PathValue:
tp, ok := p.(PathValue)
if !ok {
return fmt.Errorf("type assertion failed")
}
if !path.IsAbs(p.(PathValue).String()) {
newPath := PathValue(path.Join(basePath, tp.String()))
switch reflect.TypeOf(p).Kind() {
case reflect.Ptr:
err = pathfinder.SetValue[C](c, px, &newPath)
case reflect.String:
err = pathfinder.SetValue[C](c, px, newPath.String())
default:
err = pathfinder.SetValue[C](c, px, newPath)
}
if err != nil {
return err
}
}
}
}
return nil
}
func (s *Settings[C]) importFiles() {
defer func() {
s.notifyErrorHooks()
}()
for _, d := range s.files.directories {
fn := path.Join(d, s.files.name+s.files.format.Extension())
f, err := s.files.fs.Open(fn)
if os.IsNotExist(err) {
_ = f.Close()
continue
}
r := (io.Reader)(f)
s.importStream(reader{s.files.format, r}, func(c *C) {
_ = replacePath[*C](d, c)
})
_ = f.Close()
}
for _, f := range s.files.files {
r, err := s.files.fs.Open(f.path)
if err != nil {
s.errors = append(s.errors, err)
_ = r.Close()
continue
}
s.importStream(reader{f.format, r}, func(c *C) {
d := path.Dir(f.path)
err := replacePath[*C](d, c)
if err != nil {
return
}
})
_ = r.Close()
}
}
func (s *Settings[C]) Import() *Settings[C] {
s.Lock()
defaults := s.config
var n C
s.config = n
s.importCounter = 0
s.importFiles()
s.importStreams()
defer func() {
s.notifyErrorHooks()
}()
if err := mergo.Merge(&s.config, defaults); err != nil {
s.errors = append(s.errors, err)
}
if s.importCounter == 0 {
s.errors = append(s.errors, NoFileImportedError)
}
x := s.config
s.config = defaults
_, l := s.setConfigInternal(x)
s.Unlock()
s.notifyHooks(l)
return s
}