Files
bkl/process1.go
2024-10-13 11:27:33 -07:00

187 lines
4.3 KiB
Go

package bkl
import (
"fmt"
"strings"
)
func process1(obj any, mergeFrom *Document, mergeFromDocs []*Document, depth int) (any, error) {
depth++
if depth > 1000 {
return nil, fmt.Errorf("%#v: %w", obj, ErrCircularRef)
}
switch obj2 := obj.(type) {
case map[string]any:
return process1Map(obj2, mergeFrom, mergeFromDocs, depth)
case []any:
return process1List(obj2, mergeFrom, mergeFromDocs, depth)
case string:
return process1String(obj2, mergeFrom, mergeFromDocs, depth)
default:
return obj, nil
}
}
func process1Map(obj map[string]any, mergeFrom *Document, mergeFromDocs []*Document, depth int) (any, error) {
// Not copying obj before merge preserves the layering behavior that
// tests/merge-race relies upon.
if v, found := obj["$merge"]; found {
delete(obj, "$merge")
return process1MapMerge(obj, mergeFrom, mergeFromDocs, v, depth)
}
if found, v, obj := popMapValue(obj, "$replace"); found {
return process1MapReplace(obj, mergeFrom, mergeFromDocs, v, depth)
}
return filterMap(obj, func(k string, v any) (map[string]any, error) {
v2, err := process1(v, mergeFrom, mergeFromDocs, depth)
if err != nil {
return nil, err
}
if v2 == nil {
return map[string]any{}, nil
}
k2, err := process1(k, mergeFrom, mergeFromDocs, depth)
if err != nil {
return nil, err
}
return map[string]any{k2.(string): v2}, nil
})
}
func process1MapMerge(obj map[string]any, mergeFrom *Document, mergeFromDocs []*Document, v any, depth int) (any, error) {
in, err := get(mergeFrom, mergeFromDocs, v)
if err != nil {
return nil, err
}
next, err := mergeMap(obj, in)
if err != nil {
return nil, err
}
return process1(next, mergeFrom, mergeFromDocs, depth)
}
func process1MapReplace(obj map[string]any, mergeFrom *Document, mergeFromDocs []*Document, v any, depth int) (any, error) {
next, err := get(mergeFrom, mergeFromDocs, v)
if err != nil {
return nil, err
}
return process1(next, mergeFrom, mergeFromDocs, depth)
}
func process1List(obj []any, mergeFrom *Document, mergeFromDocs []*Document, depth int) (any, error) {
merge := []any{}
obj, err := filterList(obj, func(v any) ([]any, error) {
v2, ok := v.(map[string]any)
if !ok {
return []any{v}, nil
}
if len(v2) != 1 {
return []any{v}, nil
}
found, val, v2 := popMapValue(v2, "$merge")
if found {
merge = append(merge, val)
return nil, nil
}
return []any{v2}, nil
})
for _, m := range merge {
obj, err = process1ListMerge(obj, mergeFrom, mergeFromDocs, m, depth)
if err != nil {
return nil, err
}
}
m, obj, err := popListMapValue(obj, "$replace")
if err != nil {
return nil, err
}
if m != nil {
return process1ListReplace(obj, mergeFrom, mergeFromDocs, m, depth)
}
return filterList(obj, func(v any) ([]any, error) {
v2, err := process1(v, mergeFrom, mergeFromDocs, depth)
if err != nil {
return nil, err
}
if v2 == nil {
return nil, nil
}
return []any{v2}, nil
})
}
func process1ListMerge(obj []any, mergeFrom *Document, mergeFromDocs []*Document, m any, depth int) ([]any, error) {
in, err := get(mergeFrom, mergeFromDocs, m)
if err != nil {
return nil, err
}
return mergeList(obj, in)
}
func process1ListReplace(obj []any, mergeFrom *Document, mergeFromDocs []*Document, m any, depth int) (any, error) {
next, err := get(mergeFrom, mergeFromDocs, m)
if err != nil {
return nil, err
}
return process1(next, mergeFrom, mergeFromDocs, depth)
}
func process1String(obj string, mergeFrom *Document, mergeFromDocs []*Document, depth int) (any, error) {
if strings.HasPrefix(obj, "$merge:") {
return process1StringMerge(obj, mergeFrom, mergeFromDocs, depth)
}
if strings.HasPrefix(obj, "$replace:") {
return process1StringReplace(obj, mergeFrom, mergeFromDocs, depth)
}
return obj, nil
}
func process1StringMerge(obj string, mergeFrom *Document, mergeFromDocs []*Document, depth int) (any, error) {
path := strings.TrimPrefix(obj, "$merge:")
in, err := get(mergeFrom, mergeFromDocs, path)
if err != nil {
return nil, err
}
return process1(in, mergeFrom, mergeFromDocs, depth)
}
func process1StringReplace(obj string, mergeFrom *Document, mergeFromDocs []*Document, depth int) (any, error) {
path := strings.TrimPrefix(obj, "$replace:")
in, err := get(mergeFrom, mergeFromDocs, path)
if err != nil {
return nil, err
}
return process1(in, mergeFrom, mergeFromDocs, depth)
}