...
1
16
17 package fieldpath
18
19 import (
20 "errors"
21 "fmt"
22 "io"
23 "strconv"
24 "strings"
25
26 jsoniter "github.com/json-iterator/go"
27 "sigs.k8s.io/structured-merge-diff/v4/value"
28 )
29
30 var ErrUnknownPathElementType = errors.New("unknown path element type")
31
32 const (
33
34 peField = "f"
35
36
37 peValue = "v"
38
39
40 peIndex = "i"
41
42
43 peKey = "k"
44
45
46 peSeparator = ":"
47 )
48
49 var (
50 peFieldSepBytes = []byte(peField + peSeparator)
51 peValueSepBytes = []byte(peValue + peSeparator)
52 peIndexSepBytes = []byte(peIndex + peSeparator)
53 peKeySepBytes = []byte(peKey + peSeparator)
54 peSepBytes = []byte(peSeparator)
55 )
56
57
58 func DeserializePathElement(s string) (PathElement, error) {
59 b := []byte(s)
60 if len(b) < 2 {
61 return PathElement{}, errors.New("key must be 2 characters long:")
62 }
63 typeSep, b := b[:2], b[2:]
64 if typeSep[1] != peSepBytes[0] {
65 return PathElement{}, fmt.Errorf("missing colon: %v", s)
66 }
67 switch typeSep[0] {
68 case peFieldSepBytes[0]:
69
70
71 str := s[2:]
72 return PathElement{
73 FieldName: &str,
74 }, nil
75 case peValueSepBytes[0]:
76 iter := readPool.BorrowIterator(b)
77 defer readPool.ReturnIterator(iter)
78 v, err := value.ReadJSONIter(iter)
79 if err != nil {
80 return PathElement{}, err
81 }
82 return PathElement{Value: &v}, nil
83 case peKeySepBytes[0]:
84 iter := readPool.BorrowIterator(b)
85 defer readPool.ReturnIterator(iter)
86 fields := value.FieldList{}
87
88 iter.ReadObjectCB(func(iter *jsoniter.Iterator, key string) bool {
89 v, err := value.ReadJSONIter(iter)
90 if err != nil {
91 iter.Error = err
92 return false
93 }
94 fields = append(fields, value.Field{Name: key, Value: v})
95 return true
96 })
97 fields.Sort()
98 return PathElement{Key: &fields}, iter.Error
99 case peIndexSepBytes[0]:
100 i, err := strconv.Atoi(s[2:])
101 if err != nil {
102 return PathElement{}, err
103 }
104 return PathElement{
105 Index: &i,
106 }, nil
107 default:
108 return PathElement{}, ErrUnknownPathElementType
109 }
110 }
111
112 var (
113 readPool = jsoniter.NewIterator(jsoniter.ConfigCompatibleWithStandardLibrary).Pool()
114 writePool = jsoniter.NewStream(jsoniter.ConfigCompatibleWithStandardLibrary, nil, 1024).Pool()
115 )
116
117
118 func SerializePathElement(pe PathElement) (string, error) {
119 buf := strings.Builder{}
120 err := serializePathElementToWriter(&buf, pe)
121 return buf.String(), err
122 }
123
124 func serializePathElementToWriter(w io.Writer, pe PathElement) error {
125 stream := writePool.BorrowStream(w)
126 defer writePool.ReturnStream(stream)
127 switch {
128 case pe.FieldName != nil:
129 if _, err := stream.Write(peFieldSepBytes); err != nil {
130 return err
131 }
132 stream.WriteRaw(*pe.FieldName)
133 case pe.Key != nil:
134 if _, err := stream.Write(peKeySepBytes); err != nil {
135 return err
136 }
137 stream.WriteObjectStart()
138
139 for i, field := range *pe.Key {
140 if i > 0 {
141 stream.WriteMore()
142 }
143 stream.WriteObjectField(field.Name)
144 value.WriteJSONStream(field.Value, stream)
145 }
146 stream.WriteObjectEnd()
147 case pe.Value != nil:
148 if _, err := stream.Write(peValueSepBytes); err != nil {
149 return err
150 }
151 value.WriteJSONStream(*pe.Value, stream)
152 case pe.Index != nil:
153 if _, err := stream.Write(peIndexSepBytes); err != nil {
154 return err
155 }
156 stream.WriteInt(*pe.Index)
157 default:
158 return errors.New("invalid PathElement")
159 }
160 b := stream.Buffer()
161 err := stream.Flush()
162
163
164
165
166 stream.SetBuffer(b[:0])
167 return err
168 }
169
View as plain text