@@ -2,13 +2,15 @@ package collections
2
2
3
3
import (
4
4
"bytes"
5
+ "encoding"
5
6
"encoding/json"
6
7
"errors"
7
8
"fmt"
8
9
"iter"
9
10
"maps"
10
11
"reflect"
11
12
"slices"
13
+ "strconv"
12
14
13
15
json2 "github.com./go-json-experiment/json"
14
16
"github.com./go-json-experiment/json/jsontext"
@@ -105,6 +107,10 @@ func (m *OrderedMap[K, V]) Delete(key K) (V, bool) {
105
107
// A slice of the keys can be obtained by calling `slices.Collect`.
106
108
func (m * OrderedMap [K , V ]) Keys () iter.Seq [K ] {
107
109
return func (yield func (K ) bool ) {
110
+ if m == nil {
111
+ return
112
+ }
113
+
108
114
// We use a for loop here to ensure we enumerate new items added during iteration.
109
115
//nolint:intrange
110
116
for i := 0 ; i < len (m .keys ); i ++ {
@@ -119,6 +125,10 @@ func (m *OrderedMap[K, V]) Keys() iter.Seq[K] {
119
125
// A slice of the values can be obtained by calling `slices.Collect`.
120
126
func (m * OrderedMap [K , V ]) Values () iter.Seq [V ] {
121
127
return func (yield func (V ) bool ) {
128
+ if m == nil {
129
+ return
130
+ }
131
+
122
132
// We use a for loop here to ensure we enumerate new items added during iteration.
123
133
//nolint:intrange
124
134
for i := 0 ; i < len (m .keys ); i ++ {
@@ -132,6 +142,10 @@ func (m *OrderedMap[K, V]) Values() iter.Seq[V] {
132
142
// Entries returns an iterator over the key-value pairs in the map.
133
143
func (m * OrderedMap [K , V ]) Entries () iter.Seq2 [K , V ] {
134
144
return func (yield func (K , V ) bool ) {
145
+ if m == nil {
146
+ return
147
+ }
148
+
135
149
// We use a for loop here to ensure we enumerate new items added during iteration.
136
150
//nolint:intrange
137
151
for i := 0 ; i < len (m .keys ); i ++ {
@@ -153,11 +167,19 @@ func (m *OrderedMap[K, V]) Clear() {
153
167
154
168
// Size returns the number of key-value pairs in the map.
155
169
func (m * OrderedMap [K , V ]) Size () int {
170
+ if m == nil {
171
+ return 0
172
+ }
173
+
156
174
return len (m .keys )
157
175
}
158
176
159
177
// Clone returns a shallow copy of the map.
160
178
func (m * OrderedMap [K , V ]) Clone () * OrderedMap [K , V ] {
179
+ if m == nil {
180
+ return nil
181
+ }
182
+
161
183
m2 := m .clone ()
162
184
return & m2
163
185
}
@@ -169,6 +191,59 @@ func (m *OrderedMap[K, V]) clone() OrderedMap[K, V] {
169
191
}
170
192
}
171
193
194
+ func (m OrderedMap [K , V ]) MarshalJSON () ([]byte , error ) {
195
+ if len (m .mp ) == 0 {
196
+ return []byte ("{}" ), nil
197
+ }
198
+ var buf bytes.Buffer
199
+ buf .WriteByte ('{' )
200
+ enc := json .NewEncoder (& buf )
201
+ enc .SetEscapeHTML (false )
202
+
203
+ for i , k := range m .keys {
204
+ if i > 0 {
205
+ buf .WriteByte (',' )
206
+ }
207
+
208
+ keyString , err := resolveKeyName (reflect .ValueOf (k ))
209
+ if err != nil {
210
+ return nil , err
211
+ }
212
+
213
+ if err := enc .Encode (keyString ); err != nil {
214
+ return nil , err
215
+ }
216
+
217
+ buf .WriteByte (':' )
218
+
219
+ if err := enc .Encode (m .mp [k ]); err != nil {
220
+ return nil , err
221
+ }
222
+ }
223
+ buf .WriteByte ('}' )
224
+ return buf .Bytes (), nil
225
+ }
226
+
227
+ func resolveKeyName (k reflect.Value ) (string , error ) {
228
+ if k .Kind () == reflect .String {
229
+ return k .String (), nil
230
+ }
231
+ if tm , ok := k .Interface ().(encoding.TextMarshaler ); ok {
232
+ if k .Kind () == reflect .Pointer && k .IsNil () {
233
+ return "" , nil
234
+ }
235
+ buf , err := tm .MarshalText ()
236
+ return string (buf ), err
237
+ }
238
+ switch k .Kind () {
239
+ case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 :
240
+ return strconv .FormatInt (k .Int (), 10 ), nil
241
+ case reflect .Uint , reflect .Uint8 , reflect .Uint16 , reflect .Uint32 , reflect .Uint64 , reflect .Uintptr :
242
+ return strconv .FormatUint (k .Uint (), 10 ), nil
243
+ }
244
+ panic ("unexpected map key type" )
245
+ }
246
+
172
247
func (m * OrderedMap [K , V ]) UnmarshalJSON (data []byte ) error {
173
248
if string (data ) == "null" {
174
249
// By convention, to approximate the behavior of Unmarshal itself,
0 commit comments