mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-12-01 10:56:38 +00:00
81 lines
2.2 KiB
Go
81 lines
2.2 KiB
Go
|
package badjsonmerge
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"reflect"
|
||
|
|
||
|
"github.com/sagernet/sing-box/common/badjson"
|
||
|
"github.com/sagernet/sing-box/option"
|
||
|
E "github.com/sagernet/sing/common/exceptions"
|
||
|
)
|
||
|
|
||
|
func MergeOptions(source option.Options, destination option.Options) (option.Options, error) {
|
||
|
rawSource, err := json.Marshal(source)
|
||
|
if err != nil {
|
||
|
return option.Options{}, E.Cause(err, "marshal source")
|
||
|
}
|
||
|
rawDestination, err := json.Marshal(destination)
|
||
|
if err != nil {
|
||
|
return option.Options{}, E.Cause(err, "marshal destination")
|
||
|
}
|
||
|
rawMerged, err := MergeJSON(rawSource, rawDestination)
|
||
|
if err != nil {
|
||
|
return option.Options{}, E.Cause(err, "merge options")
|
||
|
}
|
||
|
var merged option.Options
|
||
|
err = json.Unmarshal(rawMerged, &merged)
|
||
|
if err != nil {
|
||
|
return option.Options{}, E.Cause(err, "unmarshal merged options")
|
||
|
}
|
||
|
return merged, nil
|
||
|
}
|
||
|
|
||
|
func MergeJSON(rawSource json.RawMessage, rawDestination json.RawMessage) (json.RawMessage, error) {
|
||
|
source, err := badjson.Decode(rawSource)
|
||
|
if err != nil {
|
||
|
return nil, E.Cause(err, "decode source")
|
||
|
}
|
||
|
destination, err := badjson.Decode(rawDestination)
|
||
|
if err != nil {
|
||
|
return nil, E.Cause(err, "decode destination")
|
||
|
}
|
||
|
merged, err := mergeJSON(source, destination)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return json.Marshal(merged)
|
||
|
}
|
||
|
|
||
|
func mergeJSON(anySource any, anyDestination any) (any, error) {
|
||
|
switch destination := anyDestination.(type) {
|
||
|
case badjson.JSONArray:
|
||
|
switch source := anySource.(type) {
|
||
|
case badjson.JSONArray:
|
||
|
destination = append(destination, source...)
|
||
|
default:
|
||
|
destination = append(destination, source)
|
||
|
}
|
||
|
return destination, nil
|
||
|
case *badjson.JSONObject:
|
||
|
switch source := anySource.(type) {
|
||
|
case *badjson.JSONObject:
|
||
|
for _, entry := range source.Entries() {
|
||
|
oldValue, loaded := destination.Get(entry.Key)
|
||
|
if loaded {
|
||
|
var err error
|
||
|
entry.Value, err = mergeJSON(entry.Value, oldValue)
|
||
|
if err != nil {
|
||
|
return nil, E.Cause(err, "merge object item ", entry.Key)
|
||
|
}
|
||
|
}
|
||
|
destination.Put(entry.Key, entry.Value)
|
||
|
}
|
||
|
default:
|
||
|
return nil, E.New("cannot merge json object into ", reflect.TypeOf(destination))
|
||
|
}
|
||
|
return destination, nil
|
||
|
default:
|
||
|
return destination, nil
|
||
|
}
|
||
|
}
|