Repository navigation
Expand file tree
/
Copy pathclone.go
More file actions
112 lines (106 loc) · 2.72 KB
/
Copy pathclone.go
File metadata and controls
112 lines (106 loc) · 2.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
// Copyright (c) 2026 Matthew Winter
//
// This source code is licensed under the MIT license found in the LICENSE file
// in the root directory of this source tree.
package sigma
import (
"encoding/json"
"reflect"
)
// cloneMode retains each copy boundary's map and byte-slice wire conventions.
type cloneMode uint8
const (
cloneMetadata cloneMode = iota
cloneContent
cloneProviderOptions
)
// cloneJSONValue isolates JSON-compatible containers while preserving concrete
// types. Opaque objects (including pointers and callbacks) remain caller-owned.
func cloneJSONValue(value any, mode cloneMode) any {
switch v := value.(type) {
case map[string]any:
if v == nil || len(v) == 0 && mode == cloneMetadata {
return map[string]any(nil)
}
result := make(map[string]any, len(v))
for key, item := range v {
result[key] = cloneJSONValue(item, mode)
}
return result
case Schema:
result := make(Schema, len(v))
for key, item := range v {
result[key] = cloneJSONValue(item, mode)
}
return result
case []any:
if v == nil {
return []any(nil)
}
result := make([]any, len(v))
for i, item := range v {
result[i] = cloneJSONValue(item, mode)
}
return result
case []string:
if v == nil {
return []string(nil)
}
return append(make([]string, 0, len(v)), v...)
case []byte:
if mode == cloneMetadata {
return append(v[:0:0], v...)
}
return append([]byte(nil), v...)
case json.RawMessage:
if mode == cloneMetadata {
return append(v[:0:0], v...)
}
return append(json.RawMessage(nil), v...)
case map[string]string:
if mode == cloneMetadata {
return copyStringStringMap(v)
}
}
return cloneJSONContainer(reflect.ValueOf(value), mode).Interface()
}
func cloneJSONContainer(value reflect.Value, mode cloneMode) reflect.Value {
if !value.IsValid() {
return reflect.Zero(reflect.TypeFor[any]())
}
switch value.Kind() {
case reflect.Interface:
if value.IsNil() {
return value
}
result := reflect.New(value.Type()).Elem()
result.Set(reflect.ValueOf(cloneJSONValue(value.Elem().Interface(), mode)))
return result
case reflect.Map:
if value.IsNil() {
return value
}
result := reflect.MakeMapWithSize(value.Type(), value.Len())
iter := value.MapRange()
for iter.Next() {
result.SetMapIndex(iter.Key(), cloneJSONContainer(iter.Value(), mode))
}
return result
case reflect.Slice, reflect.Array:
var result reflect.Value
if value.Kind() == reflect.Slice {
if value.IsNil() {
return value
}
result = reflect.MakeSlice(value.Type(), value.Len(), value.Len())
} else {
result = reflect.New(value.Type()).Elem()
}
for i := range value.Len() {
result.Index(i).Set(cloneJSONContainer(value.Index(i), mode))
}
return result
default:
return value
}
}