github.com/inazumav/sing-box@v0.0.0-20230926072359-ab51429a14f1/common/badjsonmerge/merge.go (about)

     1  package badjsonmerge
     2  
     3  import (
     4  	"encoding/json"
     5  	"reflect"
     6  
     7  	"github.com/inazumav/sing-box/common/badjson"
     8  	"github.com/inazumav/sing-box/option"
     9  	E "github.com/sagernet/sing/common/exceptions"
    10  )
    11  
    12  func MergeOptions(source option.Options, destination option.Options) (option.Options, error) {
    13  	rawSource, err := json.Marshal(source)
    14  	if err != nil {
    15  		return option.Options{}, E.Cause(err, "marshal source")
    16  	}
    17  	rawDestination, err := json.Marshal(destination)
    18  	if err != nil {
    19  		return option.Options{}, E.Cause(err, "marshal destination")
    20  	}
    21  	rawMerged, err := MergeJSON(rawSource, rawDestination)
    22  	if err != nil {
    23  		return option.Options{}, E.Cause(err, "merge options")
    24  	}
    25  	var merged option.Options
    26  	err = json.Unmarshal(rawMerged, &merged)
    27  	if err != nil {
    28  		return option.Options{}, E.Cause(err, "unmarshal merged options")
    29  	}
    30  	return merged, nil
    31  }
    32  
    33  func MergeJSON(rawSource json.RawMessage, rawDestination json.RawMessage) (json.RawMessage, error) {
    34  	source, err := badjson.Decode(rawSource)
    35  	if err != nil {
    36  		return nil, E.Cause(err, "decode source")
    37  	}
    38  	destination, err := badjson.Decode(rawDestination)
    39  	if err != nil {
    40  		return nil, E.Cause(err, "decode destination")
    41  	}
    42  	merged, err := mergeJSON(source, destination)
    43  	if err != nil {
    44  		return nil, err
    45  	}
    46  	return json.Marshal(merged)
    47  }
    48  
    49  func mergeJSON(anySource any, anyDestination any) (any, error) {
    50  	switch destination := anyDestination.(type) {
    51  	case badjson.JSONArray:
    52  		switch source := anySource.(type) {
    53  		case badjson.JSONArray:
    54  			destination = append(destination, source...)
    55  		default:
    56  			destination = append(destination, source)
    57  		}
    58  		return destination, nil
    59  	case *badjson.JSONObject:
    60  		switch source := anySource.(type) {
    61  		case *badjson.JSONObject:
    62  			for _, entry := range source.Entries() {
    63  				oldValue, loaded := destination.Get(entry.Key)
    64  				if loaded {
    65  					var err error
    66  					entry.Value, err = mergeJSON(entry.Value, oldValue)
    67  					if err != nil {
    68  						return nil, E.Cause(err, "merge object item ", entry.Key)
    69  					}
    70  				}
    71  				destination.Put(entry.Key, entry.Value)
    72  			}
    73  		default:
    74  			return nil, E.New("cannot merge json object into ", reflect.TypeOf(destination))
    75  		}
    76  		return destination, nil
    77  	default:
    78  		return destination, nil
    79  	}
    80  }