package util
import (
"errors"
"regexp"
"sort"
"gopkg.in/yaml.v2"
)
type RawYAML []byte
func (r *RawYAML) UnmarshalYAML(unmarshal func(interface{}) error) error {
var ms yaml.MapSlice
if err := unmarshal(&ms); err != nil {
return err
}
bb, err := yaml.Marshal(ms)
if err != nil {
return err
}
*r = bb
return nil
}
func (r RawYAML) MarshalYAML() (interface{}, error) {
return r.Map()
}
func (r RawYAML) Map() (yaml.MapSlice, error) {
var ms yaml.MapSlice
if err := yaml.Unmarshal(r, &ms); err != nil {
return nil, err
}
return ms, nil
}
func MarshalYAMLMerged(vv ...interface{}) ([]byte, error) {
var full yaml.MapSlice
for _, v := range vv {
bb, err := yaml.Marshal(v)
if err != nil {
return nil, err
}
ms, err := RawYAML(bb).Map()
if err != nil {
return nil, err
}
full = append(full, ms...)
}
return yaml.Marshal(full)
}
func UnmarshalYAMLMerged(bb []byte, vv ...interface{}) error {
var typeErrors []yaml.TypeError
for _, v := range vv {
var te *yaml.TypeError
if err := yaml.UnmarshalStrict(bb, v); errors.As(err, &te) {
typeErrors = append(typeErrors, *te)
} else if err != nil {
return err
}
if err := yaml.Unmarshal(bb, v); err != nil {
return err
}
}
var (
addedErrors = map[string]struct{}{}
notFoundErrors = map[string]int{}
)
for _, te := range typeErrors {
for _, msg := range te.Errors {
notFound := notFoundErrRegex.FindStringSubmatch(msg)
if notFound != nil {
notFoundErrors[notFound[1]]++
continue
}
addedErrors[msg] = struct{}{}
}
}
for msg, count := range notFoundErrors {
if count == len(vv) {
addedErrors[msg] = struct{}{}
}
}
if len(addedErrors) > 0 {
realErrors := make([]string, 0, len(addedErrors))
for msg := range addedErrors {
realErrors = append(realErrors, msg)
}
sort.Strings(realErrors)
return &yaml.TypeError{Errors: realErrors}
}
return nil
}
var notFoundErrRegex = regexp.MustCompile(`^(line \d+: field .* not found) in type .*$`)