@@ -30,6 +30,7 @@ const (
30
30
SchemaNullBit = 0x80 // The most signifiant bit indicates whether or not the type is nullable
31
31
)
32
32
33
+ // Schema is an interface that encodes and decodes data of a specific type
33
34
type Schema interface {
34
35
// Encode uses the schema to write the encoded value of i to the output
35
36
// stream
@@ -47,26 +48,73 @@ type Schema interface {
47
48
// stream and stores it in v
48
49
DecodeValue (r io.Reader , v reflect.Value ) error
49
50
50
- // Nullable returns true if and only if the type is nullable
51
- Nullable () bool
51
+ // GoType returns the default Go type that represents the schema
52
+ GoType () reflect.Type
53
+ }
52
54
53
- // MarshalJSON returns the JSON encoding of the schema
54
- MarshalJSON () ([]byte , error )
55
+ // Marshaler is an interface implemented by a schema, allowing it to encode
56
+ // itself into a portable binary format
57
+ type Marshaler interface {
58
+ MarshalSchemer () ([]byte , error )
59
+ }
55
60
56
- // MarshalSchemer encodes the schema in a portable binary format
57
- MarshalSchemer () []byte
61
+ // SchemaGenerator is an interface implemented by custom schema generators.
62
+ // When Register is called on a SchemaGenerator, the global SchemaOf,
63
+ // DecodeSchema, and DecodeSchemaJSON functions will call the identically
64
+ // named method on each schema generator to determine if a custom schema should
65
+ // be returned.
66
+ // If a SchemaGenerator cannot return a Schema for a specific type, it should
67
+ // return nil, nil.
68
+ // If all schema generators return a nil Schema or if Register is never called,
69
+ // then the built-in logic for returning a Schema is used.
70
+ type SchemaGenerator interface {
71
+ SchemaOfType (i interface {}) (Schema , error )
72
+ DecodeSchema (io.Reader ) (Schema , error )
73
+ DecodeSchemaJSON (io.Reader ) (Schema , error )
74
+ }
58
75
59
- // GoType returns the default Go type that represents the schema
60
- GoType () reflect.Type
76
+ type hasSchemaOfType interface {
77
+ SchemaOfType (i interface {}) (Schema , error )
78
+ }
79
+ type hasDecodeSchema interface {
80
+ DecodeSchema (io.Reader ) (Schema , error )
81
+ }
82
+ type hasDecodeSchemaJSON interface {
83
+ DecodeSchemaJSON (io.Reader ) (Schema , error )
84
+ }
85
+
86
+ var (
87
+ regSchemaOfType = []hasSchemaOfType {}
88
+ regDecodeSchema = []hasDecodeSchema {}
89
+ regDecodeSchemaJSON = []hasDecodeSchemaJSON {}
90
+ )
91
+
92
+ // Register records custom schema generators that implement `SchemaOfType`,
93
+ // `DecodeSchema`, and/or `DecodeSchemaJSON`. When `schemer.SchemaOfType` is
94
+ // called, `SchemaOfType` is called on each registered schema generator to
95
+ // determine if a custom Schema should be used for a given type.
96
+ func Register (ifaces ... interface {}) error {
97
+ for _ , iface := range ifaces {
98
+ if sg , ok := iface .(hasSchemaOfType ); ok {
99
+ regSchemaOfType = append (regSchemaOfType , sg )
100
+ }
101
+ if sg , ok := iface .(hasDecodeSchema ); ok {
102
+ regDecodeSchema = append (regDecodeSchema , sg )
103
+ }
104
+ if sg , ok := iface .(hasDecodeSchemaJSON ); ok {
105
+ regDecodeSchemaJSON = append (regDecodeSchemaJSON , sg )
106
+ }
107
+ }
61
108
}
62
109
63
- // SchemaOf returns a Schema for the specified interface value
64
- // If i is a pointer or interface type, the pointer/interface value is used to
65
- // generate the Schema. If i is nil, an zero-field FixedObjectSchema is returned.
66
- func SchemaOf (i interface {}) Schema {
110
+ // SchemaOf returns a Schema for the specified interface value.
111
+ // If i is a pointer or interface type, the value of the pointer/interface is
112
+ // used to generate the Schema.
113
+ // If i is nil, an zero-field FixedObjectSchema is returned.
114
+ func SchemaOf (i interface {}) (Schema , error ) {
67
115
if i == nil {
68
116
// Return a Schema for an empty struct
69
- return & FixedObjectSchema {}
117
+ return & FixedObjectSchema {}, nil
70
118
}
71
119
72
120
t := reflect .TypeOf (i )
@@ -80,7 +128,14 @@ func SchemaOf(i interface{}) Schema {
80
128
}
81
129
82
130
// SchemaOfType returns a Schema for the specified Go type
83
- func SchemaOfType (t reflect.Type ) Schema {
131
+ func SchemaOfType (t reflect.Type ) (Schema , error ) {
132
+ // Call registered schema generators
133
+ for _ , sg := range regSchemaOfType {
134
+ if s , err := sg .SchemaOfType (t ); s != nil || err != nil {
135
+ return s , err
136
+ }
137
+ }
138
+
84
139
nullable := false
85
140
86
141
// Dereference pointer / interface types
@@ -91,19 +146,13 @@ func SchemaOfType(t reflect.Type) Schema {
91
146
nullable = true
92
147
}
93
148
94
- for _ , s := range RegisteredSchemas () {
95
- if s .ForType (t ) != nil {
96
- return s .(Schema )
97
- }
98
- }
99
-
100
149
k := t .Kind ()
101
150
102
151
switch k {
103
152
case reflect .Bool :
104
153
s := & BoolSchema {}
105
154
s .SetNullable (nullable )
106
- return s
155
+ return s , nil
107
156
108
157
// all int types default to signed varint
109
158
case reflect .Int :
@@ -117,9 +166,9 @@ func SchemaOfType(t reflect.Type) Schema {
117
166
case reflect .Int64 :
118
167
s := & VarIntSchema {Signed : true }
119
168
s .SetNullable (nullable )
120
- return s
169
+ return s , nil
121
170
122
- // all unint types default to unsigned varint
171
+ // all uint types default to unsigned varint
123
172
case reflect .Uint :
124
173
fallthrough
125
174
case reflect .Uint8 :
@@ -131,54 +180,70 @@ func SchemaOfType(t reflect.Type) Schema {
131
180
case reflect .Uint64 :
132
181
s := & VarIntSchema {Signed : false }
133
182
s .SetNullable (nullable )
134
- return s
183
+ return s , nil
135
184
136
185
case reflect .Float32 :
137
186
s := & FloatSchema {Bits : 32 }
138
187
s .SetNullable (nullable )
139
- return s
188
+ return s , nil
140
189
case reflect .Float64 :
141
190
s := & FloatSchema {Bits : 64 }
142
191
s .SetNullable (nullable )
143
- return s
192
+ return s , nil
144
193
145
194
case reflect .Complex64 :
146
195
s := & ComplexSchema {Bits : 64 }
147
196
s .SetNullable (nullable )
148
- return s
197
+ return s , nil
149
198
150
199
case reflect .Complex128 :
151
200
s := & ComplexSchema {Bits : 128 }
152
201
s .SetNullable (nullable )
153
- return s
202
+ return s , nil
154
203
155
204
case reflect .String :
156
- s := & VarLenStringSchema {}
205
+ s := & VarStringSchema {}
157
206
s .SetNullable (nullable )
158
- return s
207
+ return s , nil
159
208
160
209
case reflect .Array :
210
+ el , err := SchemaOfType (t .Elem ())
211
+ if err != nil {
212
+ return nil , fmt .Errorf ("array type: %w" , err )
213
+ }
161
214
s := & FixedArraySchema {
162
215
Length : t .Len (),
163
- Element : SchemaOfType ( t . Elem ()) ,
216
+ Element : el ,
164
217
}
165
218
s .SetNullable (nullable )
166
- return s
219
+ return s , nil
167
220
168
221
case reflect .Slice :
222
+ el , err := SchemaOfType (t .Elem ())
223
+ if err != nil {
224
+ return nil , fmt .Errorf ("slice type: %w" , err )
225
+ }
169
226
s := & VarArraySchema {
170
- Element : SchemaOfType ( t . Elem ()) ,
227
+ Element : el ,
171
228
}
172
229
s .SetNullable (nullable )
173
- return s
230
+ return s , nil
174
231
175
232
case reflect .Map :
233
+ key , err := SchemaOfType (t .Key ())
234
+ if err != nil {
235
+ return nil , fmt .Errorf ("map key type: %w" , err )
236
+ }
237
+ val , err := SchemaOfType (t .Elem ())
238
+ if err != nil {
239
+ return nil , fmt .Errorf ("map value type: %w" , err )
240
+ }
176
241
s := & VarObjectSchema {
177
- Key : SchemaOfType ( t . Key ()) ,
178
- Value : SchemaOfType ( t . Elem ()) ,
242
+ Key : key ,
243
+ Value : val ,
179
244
}
180
245
s .SetNullable (nullable )
181
- return s
246
+ return s , nil
182
247
183
248
case reflect .Struct :
184
249
s := & FixedObjectSchema {
@@ -188,49 +253,66 @@ func SchemaOfType(t reflect.Type) Schema {
188
253
189
254
for i := 0 ; i < t .NumField (); i ++ {
190
255
f := t .Field (i )
256
+ ofs , err := SchemaOfType (f .Type )
257
+ if err != nil {
258
+ return nil , fmt .Errorf ("struct field %v: %w" , f .Name , err )
259
+ }
191
260
of := ObjectField {
192
- Schema : SchemaOfType ( f . Type ) ,
261
+ Schema : ofs ,
193
262
}
194
- if of .Schema == nil {
263
+
264
+ exported := len (f .PkgPath ) == 0
265
+ if of .Schema == nil || ! exported {
195
266
continue // skip this field
196
267
}
197
- ofSchemaOpts := of .Schema .(interface {
198
- SetNullable (bool )
199
- SetWeakDecoding (bool )
200
- })
201
268
202
269
// Parse struct tag and set aliases and schema options
203
270
tagOpts := ParseStructTag (f .Tag .Get (StructTagName ))
204
271
205
- // Note: only override option if explicitly set in the tag
206
- if tagOpts .NullableSet {
207
- ofSchemaOpts .SetNullable (tagOpts .Nullable )
208
- }
209
- if tagOpts .WeakDecodingSet {
210
- ofSchemaOpts .SetWeakDecoding (tagOpts .WeakDecoding )
211
- }
212
-
213
272
if tagOpts .FieldAliasesSet {
214
273
of .Aliases = tagOpts .FieldAliases
215
274
} else {
216
- // if no aliases set in the struct tag, use the struct field name
275
+ // if no aliases set in the tag, use the struct field name
217
276
of .Aliases = []string {f .Name }
218
277
}
219
278
220
- // check if this field is not exported
221
- exported := len (f .PkgPath ) == 0
222
- if exported && len (of .Aliases ) > 0 {
223
- s .Fields = append (s .Fields , of )
279
+ if len (of .Aliases ) == 0 {
280
+ continue // skip this field
224
281
}
282
+
283
+ // Note: only override option if explicitly set in the tag
284
+ if tagOpts .NullableSet {
285
+ if opt , ok := of .Schema .(interface {
286
+ SetNullable (bool )
287
+ }); ok {
288
+ opt .SetNullable (tagOpts .Nullable )
289
+ }
290
+ }
291
+ if tagOpts .WeakDecodingSet {
292
+ if opt , ok := of .Schema .(interface {
293
+ SetWeakDecoding (bool )
294
+ }); ok {
295
+ opt .SetWeakDecoding (tagOpts .WeakDecoding )
296
+ }
297
+ }
298
+
299
+ // Add to FixedObjectSchema field list
300
+ s .Fields = append (s .Fields , of )
225
301
}
226
- return s
302
+ return s , nil
227
303
}
228
304
229
- return nil
305
+ return nil , fmt . Errorf ( "unsupported type %v" , k )
230
306
}
231
307
232
- // DecodeJSONSchema takes a buffer of JSON data and parses it to create a schema
233
- func DecodeJSONSchema (buf []byte ) (Schema , error ) {
308
+ // DecodeSchemaJSON takes a buffer of JSON data and parses it to create a schema
309
+ func DecodeSchemaJSON (buf []byte ) (Schema , error ) {
310
+ // Call registered schema generators
311
+ for _ , sg := range regDecodeSchemaJSON {
312
+ if s , err := sg .DecodeSchemaJSON (buf ); s != nil || err != nil {
313
+ return s , err
314
+ }
315
+ }
234
316
235
317
fields := make (map [string ]interface {})
236
318
@@ -239,90 +321,89 @@ func DecodeJSONSchema(buf []byte) (Schema, error) {
239
321
return nil , err
240
322
}
241
323
242
- tmpStr , ok := fields ["type" ].(string )
324
+ // Parse `type`
325
+ tmp , ok := fields ["type" ].(string )
243
326
if ! ok {
244
- return nil , fmt .Errorf ("expected element 'type' not present in JSON data " )
327
+ return nil , fmt .Errorf ("missing or invalid schema type " )
245
328
}
246
- schemaType := strings .ToLower (tmpStr )
247
-
248
- switch schemaType {
249
-
250
- // if we encounter a custom schema, loop through all registered custom schemas
251
- // and see if any of them is a match
252
- case "custom" :
253
- for _ , s := range RegisteredSchemas () {
329
+ typeStr := strings .ToLower (tmp )
254
330
255
- customtype , ok := fields ["customtype" ].(string )
256
- if ! ok {
257
- return nil , fmt .Errorf ("invalid customtype" )
258
- }
259
-
260
- if s .Name () == customtype {
261
- s , err := s .Unmarshaljson (buf )
262
- if err != nil {
263
- return nil , err
264
- }
265
- return s , nil
266
- }
331
+ // Parse `nullable`
332
+ nullable := false
333
+ tmp , found := fields ["nullable" ]
334
+ if found {
335
+ if b , ok := tmp .(bool ); ok {
336
+ nullable = b
267
337
}
338
+ return fmt .Errorf ("nullable must be a boolean" )
339
+ }
268
340
341
+ switch typeStr {
269
342
case "bool" :
270
343
s := & BoolSchema {}
271
- b , _ := fields ["nullable" ].(bool )
272
- s .SchemaOptions .nullable = b
344
+ s .SetNullable (nullable )
273
345
return s , nil
274
346
275
347
case "int" :
276
- numBits , ok := fields ["bits" ].(float64 )
277
-
278
- // if bits are present, then we are dealing with a fixed int
348
+ bitsStr , ok := fields ["bits" ]
349
+ // if bits is present, then we are dealing with a fixed int
279
350
if ok {
351
+ bits , ok := bitsStr .(float64 )
352
+ if ! ok {
353
+ return nil , fmt .Errorf ("bits must be a number" )
354
+ }
280
355
s := & FixedIntSchema {}
356
+ s .SetNullable (nullable )
357
+ if str , ok := fields ["signed" ]; ok {
358
+ b , ok := str .(bool )
359
+ if ! ok {
360
+ return nil , fmt .Errorf ("signed must be a boolean" )
361
+ }
362
+ s .Signed = b
363
+ }
281
364
282
- b , _ := fields ["nullable" ].(bool )
283
- s .SchemaOptions .nullable = b
284
- b , _ = fields ["signed" ].(bool )
285
- s .Signed = b
286
-
287
- switch numBits {
365
+ switch bits {
288
366
case 8 :
289
367
fallthrough
290
368
case 16 :
291
369
fallthrough
292
370
case 32 :
293
371
fallthrough
294
372
case 64 :
295
- s .Bits = int (numBits )
373
+ s .Bits = int (bits )
296
374
default :
297
- return nil , fmt .Errorf ("invalid int bit size encountered in JSON data : %d" , int (numBits ))
375
+ return nil , fmt .Errorf ("invalid bit size: %d" , int (bits ))
298
376
}
299
377
300
378
return s , nil
301
379
} else {
302
380
s := & VarIntSchema {}
303
-
304
- b , _ := fields ["nullable" ].(bool )
305
- s .SchemaOptions .nullable = b
306
- b , _ = fields ["signed" ].(bool )
307
- s .Signed = b
381
+ s .SetNullable (nullable )
382
+ if str , ok := fields ["signed" ]; ok {
383
+ b , ok := str .(bool )
384
+ if ! ok {
385
+ return nil , fmt .Errorf ("signed must be a boolean" )
386
+ }
387
+ s .Signed = b
388
+ }
308
389
309
390
return s , nil
310
391
}
311
392
312
393
case "float" :
313
394
s := & FloatSchema {}
314
- numBits , ok := fields ["bits" ].(float64 )
395
+ bits , ok := fields ["bits" ].(float64 )
315
396
316
397
if ! ok {
317
398
return nil , fmt .Errorf ("bits not present for float type in JSON data" )
318
399
}
319
400
320
- if numBits == 64 {
401
+ if bits == 64 {
321
402
s .Bits = 64
322
- } else if numBits == 32 {
403
+ } else if bits == 32 {
323
404
s .Bits = 32
324
405
} else {
325
- return nil , fmt .Errorf ("invalid float bit size encountered in JSON data: %d" , int (numBits ))
406
+ return nil , fmt .Errorf ("invalid float bit size encountered in JSON data: %d" , int (bits ))
326
407
}
327
408
328
409
b , _ := fields ["nullable" ].(bool )
@@ -332,18 +413,18 @@ func DecodeJSONSchema(buf []byte) (Schema, error) {
332
413
333
414
case "complex" :
334
415
s := & ComplexSchema {}
335
- numBits , ok := fields ["bits" ].(float64 )
416
+ bits , ok := fields ["bits" ].(float64 )
336
417
337
418
if ! ok {
338
419
return nil , fmt .Errorf ("bits not present for complex type in JSON data" )
339
420
}
340
421
341
- if numBits == 128 {
422
+ if bits == 128 {
342
423
s .Bits = 128
343
- } else if numBits == 64 {
424
+ } else if bits == 64 {
344
425
s .Bits = 64
345
426
} else {
346
- return nil , fmt .Errorf ("invalid complex bit size encountered in JSON data: %d" , int (numBits ))
427
+ return nil , fmt .Errorf ("invalid complex bit size encountered in JSON data: %d" , int (bits ))
347
428
}
348
429
b , _ := fields ["nullable" ].(bool )
349
430
s .SchemaOptions .nullable = b
@@ -418,7 +499,7 @@ func DecodeJSONSchema(buf []byte) (Schema, error) {
418
499
return nil , err
419
500
}
420
501
421
- s .Element , err = DecodeJSONSchema (tmp )
502
+ s .Element , err = DecodeSchemaJSON (tmp )
422
503
if err != nil {
423
504
return nil , err
424
505
}
@@ -436,7 +517,7 @@ func DecodeJSONSchema(buf []byte) (Schema, error) {
436
517
return nil , err
437
518
}
438
519
439
- s .Element , err = DecodeJSONSchema (tmp )
520
+ s .Element , err = DecodeSchemaJSON (tmp )
440
521
if err != nil {
441
522
return nil , err
442
523
}
@@ -481,7 +562,7 @@ func DecodeJSONSchema(buf []byte) (Schema, error) {
481
562
return nil , err
482
563
}
483
564
// recursive call to process this field of this object...
484
- of .Schema , err = DecodeJSONSchema (tmp )
565
+ of .Schema , err = DecodeSchemaJSON (tmp )
485
566
if err != nil {
486
567
return nil , err
487
568
}
@@ -500,7 +581,7 @@ func DecodeJSONSchema(buf []byte) (Schema, error) {
500
581
if err != nil {
501
582
return nil , err
502
583
}
503
- s .Key , err = DecodeJSONSchema (tmp )
584
+ s .Key , err = DecodeSchemaJSON (tmp )
504
585
if err != nil {
505
586
return nil , err
506
587
}
@@ -509,7 +590,7 @@ func DecodeJSONSchema(buf []byte) (Schema, error) {
509
590
if err != nil {
510
591
return nil , err
511
592
}
512
- s .Value , err = DecodeJSONSchema (tmp )
593
+ s .Value , err = DecodeSchemaJSON (tmp )
513
594
if err != nil {
514
595
return nil , err
515
596
}
@@ -519,18 +600,13 @@ func DecodeJSONSchema(buf []byte) (Schema, error) {
519
600
}
520
601
}
521
602
522
- return nil , fmt .Errorf ("invalid JSON schema type: %s" , schemaType )
603
+ return nil , fmt .Errorf ("invalid schema type: %s" , typeStr )
523
604
}
524
605
525
606
// DecodeSchema decodes a Schema from its binary representation
526
- func DecodeSchema (buf []byte ) (Schema , error ) {
527
-
528
- byteIndex := 0 // always start at the beginning of the buffer
529
-
530
- // and then decodeSchema() will advance *byteIndex as it goes
531
- tmp , err := decodeSchema (buf , & byteIndex )
532
-
533
- return tmp , err
607
+ func DecodeSchema (buf []byte ) (Schema , int , error ) {
608
+ byteIndex := 0
609
+ return decodeSchema (buf , & byteIndex )
534
610
}
535
611
536
612
// decodeSchema processes buf[] to actually decode the binary schema.
@@ -820,13 +896,13 @@ func decodeSchema(buf []byte, byteIndex *int) (Schema, error) {
820
896
// nullable.
821
897
// If this routine returns false, no more processing is needed on the
822
898
// encoder who called this routine.
823
- func PreEncode (s Schema , w io.Writer , v * reflect.Value ) (bool , error ) {
899
+ func PreEncode (nullable bool , w io.Writer , v * reflect.Value ) (bool , error ) {
824
900
// Dereference pointer / interface types
825
901
for k := v .Kind (); k == reflect .Ptr || k == reflect .Interface ; k = v .Kind () {
826
902
* v = v .Elem ()
827
903
}
828
904
829
- if s . Nullable () {
905
+ if nullable {
830
906
// did the caller pass in a nil value, or a null pointer?
831
907
if ! v .IsValid () {
832
908
// per the revised spec, 1 indicates null
@@ -852,15 +928,15 @@ func PreEncode(s Schema, w io.Writer, v *reflect.Value) (bool, error) {
852
928
// is nullable.
853
929
// This routine also handles derefering pointers and interfaces, and returns
854
930
// the new value of v after it is set.
855
- func PreDecode (s Schema , r io.Reader , v reflect.Value ) (reflect.Value , error ) {
931
+ func PreDecode (nullable bool , r io.Reader , v reflect.Value ) (reflect.Value , error ) {
856
932
// if t is a ptr or interface type, remove exactly ONE level of indirection
857
933
if k := v .Kind (); ! v .CanSet () && (k == reflect .Ptr || k == reflect .Interface ) {
858
934
v = v .Elem ()
859
935
}
860
936
861
937
// if the data indicates this type is nullable, then the actual
862
938
// value is preceeded by one byte [which indicates if the encoder encoded a nill value]
863
- if s . Nullable () {
939
+ if nullable {
864
940
buf := make ([]byte , 1 )
865
941
866
942
// first byte indicates whether value is null or not...
@@ -903,6 +979,7 @@ func PreDecode(s Schema, r io.Reader, v reflect.Value) (reflect.Value, error) {
903
979
904
980
// initialization function for the Schemer Library
905
981
func init () {
906
- RegisterCustomSchema (& dateSchema {})
907
- RegisterCustomSchema (& ipv4Schema {})
982
+ Register (dateSchemaGenerator {})
983
+ // RegisterCustomSchema(&dateSchema{})
984
+ // RegisterCustomSchema(&ipv4Schema{})
908
985
}
0 commit comments