Skip to content

Commit 37fa70a

Browse files
committed
Pass hints, add infer support form strings
1 parent d08b492 commit 37fa70a

File tree

4 files changed

+76
-62
lines changed

4 files changed

+76
-62
lines changed

hints.go

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,6 @@ type Hints struct {
99
Discriminator HintSet
1010
}
1111

12-
func New(defaultNumType NumType, enums, values, discriminator HintSet) Hints {
13-
return Hints{
14-
DefaultNumType: defaultNumType,
15-
Enums: enums,
16-
Values: values,
17-
Discriminator: discriminator,
18-
}
19-
}
20-
2112
func (h Hints) SubHints(key string) Hints {
2213
return Hints{
2314
DefaultNumType: h.DefaultNumType,
@@ -43,12 +34,17 @@ type HintSet struct {
4334
Values [][]string
4435
}
4536

46-
func NewHintSet(values [][]string) HintSet {
37+
func NewHintSet() HintSet {
4738
return HintSet{
48-
Values: values,
39+
Values: [][]string{},
4940
}
5041
}
5142

43+
func (h HintSet) Add(v []string) HintSet {
44+
h.Values = append(h.Values, v)
45+
return h
46+
}
47+
5248
func (h HintSet) SubHints(key string) HintSet {
5349
filteredValues := [][]string{}
5450

inferred_schema.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ func (i *InferredSchema) Infer(value any, hints Hints) *InferredSchema {
276276
if m, ok := value.(map[string]any); ok && i.SchemaType == SchemaTypeValues {
277277
subInfer := i.Values
278278
for k, v := range m {
279-
subInfer = NewInferredSchema().Infer(v, hints.SubHints(k))
279+
subInfer = subInfer.Infer(v, hints.SubHints(k))
280280
}
281281

282282
return &InferredSchema{
@@ -314,7 +314,7 @@ func (i *InferredSchema) Infer(value any, hints Hints) *InferredSchema {
314314
}
315315

316316
// IntoSchema will convert an `InferredSchema` to a final `Schema`.
317-
func (i *InferredSchema) IntoSchema() Schema {
317+
func (i *InferredSchema) IntoSchema(hints Hints) Schema {
318318
switch i.SchemaType {
319319
case SchemaTypeUnknown, SchemaTypeAny:
320320
return Schema{}
@@ -323,7 +323,7 @@ func (i *InferredSchema) IntoSchema() Schema {
323323
return Schema{Type: jtd.TypeBoolean}
324324
case SchemaTypeNumber:
325325
return Schema{
326-
Type: i.Number.IntoType(NumTypeUint8),
326+
Type: i.Number.IntoType(hints.DefaultNumType),
327327
}
328328
case SchemaTypeString:
329329
return Schema{Type: jtd.TypeString}
@@ -337,7 +337,7 @@ func (i *InferredSchema) IntoSchema() Schema {
337337

338338
return Schema{Enum: enum}
339339
case SchemaTypeArray:
340-
elements := i.Array.IntoSchema()
340+
elements := i.Array.IntoSchema(hints)
341341
return Schema{Elements: &elements}
342342
case SchemaTypeProperties:
343343
var (
@@ -349,15 +349,15 @@ func (i *InferredSchema) IntoSchema() Schema {
349349
required = make(map[string]Schema, len(i.Properties.Required))
350350

351351
for k, v := range i.Properties.Required {
352-
required[k] = v.IntoSchema()
352+
required[k] = v.IntoSchema(hints)
353353
}
354354
}
355355

356356
if i.Properties.Optional != nil {
357357
optional = make(map[string]Schema, len(i.Properties.Optional))
358358

359359
for k, v := range i.Properties.Optional {
360-
optional[k] = v.IntoSchema()
360+
optional[k] = v.IntoSchema(hints)
361361
}
362362
}
363363

@@ -366,20 +366,20 @@ func (i *InferredSchema) IntoSchema() Schema {
366366
OptionalProperties: optional,
367367
}
368368
case SchemaTypeValues:
369-
values := i.Values.IntoSchema()
369+
values := i.Values.IntoSchema(hints)
370370
return Schema{Values: &values}
371371
case SchemaTypeDiscriminator:
372372
mappings := map[string]Schema{}
373373
for k, v := range i.Discriminator.Mapping {
374-
mappings[k] = v.IntoSchema()
374+
mappings[k] = v.IntoSchema(hints)
375375
}
376376

377377
return Schema{
378378
Discriminator: i.Discriminator.Discriminator,
379379
Mapping: mappings,
380380
}
381381
case SchemaTypeNullable:
382-
schema := i.Nullable.IntoSchema()
382+
schema := i.Nullable.IntoSchema(hints)
383383
schema.Nullable = true
384384

385385
return schema

inferrer.go

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package jtdinfer
22

3+
import (
4+
"encoding/json"
5+
)
6+
37
// Inferrer represents the `InferredSchema` with its state combined with the
48
// hints used when inferring.
59
type Inferrer struct {
@@ -24,6 +28,31 @@ func (i *Inferrer) Infer(value any) *Inferrer {
2428
}
2529

2630
// IntoSchema will convert the `InferredSchema` into a final `Schema`.
27-
func (i *Inferrer) IntoSchema() Schema {
28-
return i.Inference.IntoSchema()
31+
func (i *Inferrer) IntoSchema(hints Hints) Schema {
32+
return i.Inference.IntoSchema(hints)
33+
}
34+
35+
// InferStrings accepts a slice of strings and will convert them to either a
36+
// `map[string]any` or []any` and run inference on all the rows. If any of the
37+
// rows are not valid JSON object or list, the inference up to that point is
38+
// returned.
39+
//
40+
// If you need to infer simple values like strings or integers they can be
41+
// passed directly to `Infer`.
42+
func InferStrings(rows []string, hints Hints) *Inferrer {
43+
inferrer := NewInferrer(hints)
44+
45+
for _, row := range rows {
46+
var toInfer any = make(map[string]any, 0)
47+
if err := json.Unmarshal([]byte(row), &toInfer); err != nil {
48+
toInfer = make([]any, 0)
49+
if err := json.Unmarshal([]byte(row), &toInfer); err != nil {
50+
return inferrer
51+
}
52+
}
53+
54+
inferrer = inferrer.Infer(toInfer)
55+
}
56+
57+
return inferrer
2958
}

inferrer_test.go

Lines changed: 29 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,41 @@
11
package jtdinfer
22

33
import (
4-
"encoding/json"
54
"testing"
65

76
jtd "github.com/jsontypedef/json-typedef-go"
87
"github.com/stretchr/testify/assert"
9-
"github.com/stretchr/testify/require"
108
)
119

1210
func TestJTDInfer(t *testing.T) {
1311
rows := []string{
1412
`{"name": "Joe", "age": 42, "hobbies": ["code", "animals"]}`,
1513
}
1614

17-
inferrer := NewInferrer(Hints{})
18-
19-
for _, row := range rows {
20-
rowAsJSON := make(map[string]any, 0)
21-
require.NoError(t, json.Unmarshal([]byte(row), &rowAsJSON))
22-
inferrer = inferrer.Infer(rowAsJSON)
23-
}
24-
2515
expectedSchema := Schema{
2616
Properties: map[string]Schema{
2717
"name": {Type: jtd.TypeString},
2818
"age": {Type: jtd.TypeUint8},
2919
"hobbies": {Elements: &Schema{Type: jtd.TypeString}},
3020
},
3121
}
32-
gotSchema := inferrer.IntoSchema()
22+
gotSchema := InferStrings(rows, Hints{}).IntoSchema(Hints{})
3323

3424
assert.EqualValues(t, expectedSchema, gotSchema)
3525
}
3626

37-
func TestJTDInferrerWithHints(t *testing.T) {
27+
func TestJTDInferrerWithEnumHints(t *testing.T) {
3828
hints := Hints{
39-
Enums: HintSet{
40-
Values: [][]string{
41-
{"name"},
42-
{"address", "city"},
43-
},
44-
},
29+
Enums: NewHintSet().
30+
Add([]string{"name"}).
31+
Add([]string{"address", "city"}),
4532
}
4633

4734
rows := []string{
4835
`{"address": {"city": "Stockholm"}, "name": "Joe", "age": 42}`,
4936
`{"address": {"city": "Umeå"}, "name": "Labero", "age": 42}`,
5037
}
5138

52-
inferrer := NewInferrer(hints)
53-
54-
for _, row := range rows {
55-
rowAsJSON := make(map[string]any, 0)
56-
require.NoError(t, json.Unmarshal([]byte(row), &rowAsJSON))
57-
inferrer = inferrer.Infer(rowAsJSON)
58-
}
59-
6039
expectedSchema := Schema{
6140
Properties: map[string]Schema{
6241
"name": {Enum: []string{"Joe", "Labero"}},
@@ -68,30 +47,40 @@ func TestJTDInferrerWithHints(t *testing.T) {
6847
},
6948
},
7049
}
71-
gotSchema := inferrer.IntoSchema()
50+
gotSchema := InferStrings(rows, hints).IntoSchema(hints)
7251

7352
assert.EqualValues(t, expectedSchema, gotSchema)
7453
}
7554

76-
func TestJTDInferWithDiscriminatorHints(t *testing.T) {
55+
func TestJTDInferWithValuesHints(t *testing.T) {
7756
hints := Hints{
78-
Discriminator: HintSet{
79-
Values: [][]string{
80-
{"-", "type"},
81-
},
82-
},
57+
Values: NewHintSet().Add([]string{}),
8358
}
8459

8560
rows := []string{
86-
`[{"type": "s", "value": "foo"},{"type": "n", "value": 3.14}]`,
61+
`{"x": [1, 2, 3], "y": [4, 5, 6], "z": [7, 8, 9]}`,
62+
`{"x": [1, 2, 3], "y": [4, 5, -600], "z": [7, 8, 9]}`,
8763
}
8864

89-
inferrer := NewInferrer(hints)
65+
expectedSchema := Schema{
66+
Values: &Schema{
67+
Elements: &Schema{
68+
Type: jtd.TypeInt16,
69+
},
70+
},
71+
}
72+
gotSchema := InferStrings(rows, hints).IntoSchema(hints)
73+
74+
assert.EqualValues(t, expectedSchema, gotSchema)
75+
}
9076

91-
for _, row := range rows {
92-
rowAsJSON := make([]any, 0)
93-
require.NoError(t, json.Unmarshal([]byte(row), &rowAsJSON))
94-
inferrer = inferrer.Infer(rowAsJSON)
77+
func TestJTDInferWithDiscriminatorHints(t *testing.T) {
78+
hints := Hints{
79+
Discriminator: NewHintSet().Add([]string{"-", "type"}),
80+
}
81+
82+
rows := []string{
83+
`[{"type": "s", "value": "foo"},{"type": "n", "value": 3.14}]`,
9584
}
9685

9786
expectedSchema := Schema{
@@ -111,7 +100,7 @@ func TestJTDInferWithDiscriminatorHints(t *testing.T) {
111100
},
112101
},
113102
}
114-
gotSchema := inferrer.IntoSchema()
103+
gotSchema := InferStrings(rows, hints).IntoSchema(hints)
115104

116105
assert.EqualValues(t, expectedSchema, gotSchema)
117106
}

0 commit comments

Comments
 (0)