local-server/cmd/dkl-dir2config/assemble.go
2024-11-05 11:53:23 +01:00

156 lines
2.4 KiB
Go

package main
import (
"bytes"
"fmt"
"io"
"log"
"os"
"os/exec"
"strings"
"gopkg.in/yaml.v2"
)
func mergeIn(tgt, add map[any]any) {
mergeLoop:
for k, v := range add {
switch v := v.(type) {
case map[any]any:
if tgtV, ok := tgt[k]; ok {
switch tgtV := tgtV.(type) {
case map[any]any:
mergeIn(tgtV, v)
continue mergeLoop
}
}
}
tgt[k] = v
}
}
func assemble(path string) (yamlBytes []byte, err error) {
obj := map[any]any{}
if Debug {
log.Printf("assemble %q", path)
}
err = eachFragment(path, searchList, func(r io.Reader) (err error) {
m := map[any]any{}
err = yaml.NewDecoder(r).Decode(&m)
if err != nil {
return
}
mergeIn(obj, m)
return
})
if err != nil {
err = fmt.Errorf("failed to assemble %q: %w", path, err)
return
}
yamlBytes, err = yaml.Marshal(obj)
if err != nil {
return
}
if Debug {
log.Printf("assemble %q result:\n%s", path, yamlBytes)
}
return
}
func eachFragment(path string, searchList []FS, walk func(io.Reader) error) (err error) {
var r io.ReadCloser
for len(searchList) != 0 {
fs := searchList[0]
r, err = fs.Open(path + ".yaml")
if os.IsNotExist(err) {
searchList = searchList[1:]
continue
}
if err != nil {
return
}
// found and open
break
}
if r == nil {
err = fmt.Errorf("%s: %w", path, os.ErrNotExist)
return
}
ba, err := io.ReadAll(r)
r.Close()
if err != nil {
return
}
if Debug {
log.Print("fragment:\n", string(ba))
}
in := bytes.NewBuffer(ba)
for {
var line string
line, err = in.ReadString('\n')
if err == io.EOF {
break
}
if err != nil {
return
}
line = strings.TrimSpace(line)
if len(line) == 0 {
continue
}
genCmd, found := strings.CutPrefix(line, "#!gen ")
if found {
cmdArgs := strings.Fields(genCmd)
if Debug {
log.Print("#!gen ", cmdArgs)
}
cmd := "gen/" + cmdArgs[0]
args := cmdArgs[1:]
genOutput, err := exec.Command(cmd, args...).Output()
if err != nil {
return fmt.Errorf("gen %v: %w", cmdArgs, err)
}
walk(bytes.NewBuffer(genOutput))
continue
}
includePath, found := strings.CutPrefix(line, "#!include ")
if !found {
continue
}
includePath = strings.TrimSpace(includePath)
if Debug {
log.Print("#!include ", includePath)
}
err = eachFragment(includePath, searchList, walk)
if err != nil {
return fmt.Errorf("include %q: %w", includePath, err)
}
}
in = bytes.NewBuffer(ba)
err = walk(in)
return
}