Skip to content

Commit d991ab4

Browse files
committed
refactor: migrate go-to-schema reflection to x/jsonschema
Replace the invopop/jsonschema reflector with go.jacobcolvin.com/x/ jsonschema, unifying the package on the google/jsonschema-go data model already used elsewhere. WithNullable(false) keeps slices, maps, and pointers non-nullable so KCL generation renders element types instead of any, and WithDefinitions(false) inlines a single schema. Collapse the Reflector type into a package-level Reflect function with a WithGoComments option, dropping the per-package newSchemaReflector wrappers. Generated KCL output is byte-identical.
1 parent 00366e3 commit d991ab4

12 files changed

Lines changed: 158 additions & 102 deletions

File tree

go.mod

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ require (
1111
github.com/charmbracelet/x/exp/golden v0.0.0-20260602025833-85a30b5e440a
1212
github.com/getkin/kin-openapi v0.140.0
1313
github.com/iancoleman/strcase v0.3.0
14-
github.com/invopop/jsonschema v0.14.0
1514
github.com/klauspost/compress v1.18.6
1615
github.com/mattn/go-isatty v0.0.22
1716
github.com/spf13/cobra v1.10.2
1817
github.com/stretchr/testify v1.11.1
1918
go.jacobcolvin.com/niceyaml v0.0.0-20260606070754-4b3c7a02856f
2019
go.jacobcolvin.com/x/cobras v0.0.0-20260607023654-2c95c01f5fe6
20+
go.jacobcolvin.com/x/jsonschema v0.0.0-20260608132324-4aebb4523229
2121
go.jacobcolvin.com/x/stringtest v0.1.0
2222
go.jacobcolvin.com/x/version v0.1.0
2323
golang.org/x/sync v0.20.0
@@ -212,7 +212,6 @@ require (
212212
github.com/opencontainers/runtime-spec v1.2.1 // indirect
213213
github.com/otiai10/copy v1.14.1 // indirect
214214
github.com/otiai10/mint v1.6.3 // indirect
215-
github.com/pb33f/ordered-map/v2 v2.3.1 // indirect
216215
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
217216
github.com/pjbgf/sha1cd v0.3.2 // indirect
218217
github.com/pkg/errors v0.9.1 // indirect
@@ -254,16 +253,15 @@ require (
254253
go.opentelemetry.io/otel/trace v1.43.0 // indirect
255254
go.yaml.in/yaml/v2 v2.4.4 // indirect
256255
go.yaml.in/yaml/v3 v3.0.4 // indirect
257-
go.yaml.in/yaml/v4 v4.0.0-rc.2 // indirect
258256
golang.org/x/crypto v0.51.0 // indirect
259257
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 // indirect
260-
golang.org/x/mod v0.35.0 // indirect
261-
golang.org/x/net v0.53.0 // indirect
258+
golang.org/x/mod v0.36.0 // indirect
259+
golang.org/x/net v0.55.0 // indirect
262260
golang.org/x/oauth2 v0.35.0 // indirect
263261
golang.org/x/sys v0.45.0 // indirect
264262
golang.org/x/term v0.43.0 // indirect
265263
golang.org/x/time v0.14.0 // indirect
266-
golang.org/x/tools v0.44.0 // indirect
264+
golang.org/x/tools v0.45.0 // indirect
267265
google.golang.org/api v0.171.0 // indirect
268266
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect
269267
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect

go.sum

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -411,8 +411,6 @@ github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSAS
411411
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
412412
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
413413
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
414-
github.com/invopop/jsonschema v0.14.0 h1:MHQqLhvpNUZfw+hM3AZDYK7jxO8FZoQeQM77g8iyZjg=
415-
github.com/invopop/jsonschema v0.14.0/go.mod h1:ygm6C2EaVNMBDPpaPlnOA2pFAxBnxGjFlMZABxm9n2I=
416414
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
417415
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
418416
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
@@ -537,8 +535,6 @@ github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8=
537535
github.com/otiai10/copy v1.14.1/go.mod h1:oQwrEDDOci3IM8dJF0d8+jnbfPDllW6vUjNc3DoZm9I=
538536
github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs=
539537
github.com/otiai10/mint v1.6.3/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
540-
github.com/pb33f/ordered-map/v2 v2.3.1 h1:5319HDO0aw4DA4gzi+zv4FXU9UlSs3xGZ40wcP1nBjY=
541-
github.com/pb33f/ordered-map/v2 v2.3.1/go.mod h1:qxFQgd0PkVUtOMCkTapqotNgzRhMPL7VvaHKbd1HnmQ=
542538
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
543539
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
544540
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI=
@@ -661,6 +657,8 @@ go.jacobcolvin.com/niceyaml v0.0.0-20260606070754-4b3c7a02856f h1:LWQ/e5B+1OPKgx
661657
go.jacobcolvin.com/niceyaml v0.0.0-20260606070754-4b3c7a02856f/go.mod h1:IKkRz7ha8uLG2G7lknugdpRu4V5VNE271oZ1LoCwEYc=
662658
go.jacobcolvin.com/x/cobras v0.0.0-20260607023654-2c95c01f5fe6 h1:IKBXtYed5sIROWOFkaS6iYD2NMG0j9MvgX/8q6X6Lak=
663659
go.jacobcolvin.com/x/cobras v0.0.0-20260607023654-2c95c01f5fe6/go.mod h1:LLWfBYoVQ1eoAd3meKiEtqaJpcWuXUCECoHv5MZQZy0=
660+
go.jacobcolvin.com/x/jsonschema v0.0.0-20260608132324-4aebb4523229 h1:1PruhwL+wi0wOEXMysDrlejgfoO2MMFGk+He0O+Ckk0=
661+
go.jacobcolvin.com/x/jsonschema v0.0.0-20260608132324-4aebb4523229/go.mod h1:lmSjzo4LijaWC7NRu2bo+P2MmPYzQlafIQ/+sJQkEbA=
664662
go.jacobcolvin.com/x/magicschema v0.1.1-0.20260607203632-537f97dbdef0 h1:Ek4Mz9x5WWVnjKYGSTadUvicnCEopG5AWp7Xfi7vczk=
665663
go.jacobcolvin.com/x/magicschema v0.1.1-0.20260607203632-537f97dbdef0/go.mod h1:T+Iu5PFn7QXm78s80VLG3lGdX0pZqHlO2twgyVeaqe8=
666664
go.jacobcolvin.com/x/stringtest v0.1.0 h1:CokQSICcFmywBapU5p1SsA2YBD+aSL3R/EPM1FRv5IM=
@@ -729,8 +727,6 @@ go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ=
729727
go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ=
730728
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
731729
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
732-
go.yaml.in/yaml/v4 v4.0.0-rc.2 h1:/FrI8D64VSr4HtGIlUtlFMGsm7H7pWTbj6vOLVZcA6s=
733-
go.yaml.in/yaml/v4 v4.0.0-rc.2/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0=
734730
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
735731
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
736732
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@@ -743,8 +739,8 @@ golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67/go.mod h1:qj5a5QZpwLU2NLQudw
743739
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
744740
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
745741
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
746-
golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=
747-
golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
742+
golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4=
743+
golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ=
748744
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
749745
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
750746
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -754,8 +750,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
754750
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
755751
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
756752
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
757-
golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
758-
golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
753+
golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
754+
golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
759755
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
760756
golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ=
761757
golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
@@ -796,8 +792,8 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm
796792
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
797793
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
798794
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
799-
golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c=
800-
golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI=
795+
golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8=
796+
golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0=
801797
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
802798
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
803799
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=

pkg/jsonschema/reflector.go

Lines changed: 129 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,58 +2,99 @@ package jsonschema
22

33
import (
44
"bytes"
5+
"encoding/json"
56
"fmt"
67
"io"
78
"reflect"
89
"regexp"
910

10-
invopopjsonschema "github.com/invopop/jsonschema"
11+
gojsonschema "github.com/google/jsonschema-go/jsonschema"
12+
xjsonschema "go.jacobcolvin.com/x/jsonschema"
1113

1214
"github.com/macropower/kclipper/pkg/kclgen"
1315
)
1416

15-
type Reflector struct {
16-
Reflector *invopopjsonschema.Reflector
17+
// reflectConfig holds the options for a [Reflect] call.
18+
type reflectConfig struct {
19+
comments bool
1720
}
1821

19-
func NewReflector() *Reflector {
20-
return &Reflector{
21-
Reflector: &invopopjsonschema.Reflector{
22-
DoNotReference: true,
23-
ExpandedStruct: true,
24-
},
22+
// ReflectOpt configures [Reflect].
23+
//
24+
// Available options:
25+
// - [WithGoComments]
26+
type ReflectOpt func(*reflectConfig)
27+
28+
// WithGoComments is a [ReflectOpt] that enables extraction of Go doc comments
29+
// as schema descriptions. The source files of the reflected types must be
30+
// resolvable at generation time.
31+
func WithGoComments() ReflectOpt {
32+
return func(c *reflectConfig) {
33+
c.comments = true
2534
}
2635
}
2736

28-
func (r *Reflector) AddGoComments(pkg, path string) error {
29-
err := r.Reflector.AddGoComments(pkg, path, invopopjsonschema.WithFullComment())
37+
// Reflect generates a schema for t and returns it wrapped in a [Reflected].
38+
//
39+
// Definitions are inlined rather than referenced via $ref, and nil-able Go
40+
// types are not made nullable, so the result is a single self-contained,
41+
// non-nullable schema suitable for KCL schema generation (a multi-type union
42+
// with null would otherwise collapse to any).
43+
func Reflect(t reflect.Type, opts ...ReflectOpt) (*Reflected, error) {
44+
var cfg reflectConfig
45+
46+
for _, opt := range opts {
47+
opt(&cfg)
48+
}
49+
50+
genOpts := []xjsonschema.Option{
51+
// Inline every type instead of emitting a $defs/$ref graph; KCL schema
52+
// generation consumes a single flattened schema.
53+
xjsonschema.WithDefinitions(false),
54+
// Keep slices, maps, and pointers non-nullable; KCL schema generation
55+
// renders a type union with null as any, losing the element type.
56+
xjsonschema.WithNullable(false),
57+
}
58+
if cfg.comments {
59+
genOpts = append(genOpts, xjsonschema.WithComments(true))
60+
}
61+
62+
s, err := xjsonschema.Generate(t, genOpts...)
3063
if err != nil {
31-
return fmt.Errorf("add go comments from %q: %w", pkg, err)
64+
return nil, fmt.Errorf("reflect %s: %w", t, err)
3265
}
3366

34-
return nil
67+
return &Reflected{Schema: s, name: typeName(t)}, nil
3568
}
3669

37-
func (r *Reflector) Reflect(t reflect.Type, opts ...PropertyOpt) *Reflected {
38-
rs := r.Reflector.ReflectFromType(t)
39-
for _, opt := range opts {
40-
opt(rs)
70+
// typeName returns the schema name for t, dereferencing pointers. KCL schema
71+
// generation names the root schema after this value, since the inlined schema
72+
// carries no $id for it to use instead.
73+
func typeName(t reflect.Type) string {
74+
for t.Kind() == reflect.Pointer {
75+
t = t.Elem()
4176
}
4277

43-
return &Reflected{Schema: rs}
78+
return t.Name()
4479
}
4580

4681
type replacement struct {
4782
re *regexp.Regexp
4883
repl string
4984
}
5085

86+
// Reflected is a JSON Schema produced by [Reflect], ready to be converted to a
87+
// KCL schema. Create instances with [Reflect].
5188
type Reflected struct {
52-
Schema *invopopjsonschema.Schema
89+
// Schema is the underlying JSON Schema. It can be adjusted directly or via
90+
// [Reflected.SetProperty] and related methods before KCL generation.
91+
Schema *gojsonschema.Schema
5392

93+
name string
5494
replacements []replacement
5595
}
5696

97+
// GenerateKCL converts the schema to a KCL schema and writes it to w.
5798
func (r *Reflected) GenerateKCL(w io.Writer, opts ...GenOpt) error {
5899
for _, opt := range opts {
59100
opt(r)
@@ -64,8 +105,15 @@ func (r *Reflected) GenerateKCL(w io.Writer, opts ...GenOpt) error {
64105
return fmt.Errorf("marshal json schema: %w", err)
65106
}
66107

108+
// KCL schema generation names the root schema after the file name when the
109+
// schema has no $id; pass the reflected type name so the schema keeps it.
110+
filename := r.name
111+
if filename == "" {
112+
filename = "chart"
113+
}
114+
67115
b := &bytes.Buffer{}
68-
err = kclgen.Gen.GenKcl(b, "chart", jsBytes, &kclgen.GenKclOptions{
116+
err = kclgen.Gen.GenKcl(b, filename, jsBytes, &kclgen.GenKclOptions{
69117
Mode: kclgen.ModeJSONSchema,
70118
CastingOption: kclgen.OriginalName,
71119
})
@@ -87,77 +135,120 @@ func (r *Reflected) GenerateKCL(w io.Writer, opts ...GenOpt) error {
87135
return nil
88136
}
89137

138+
// SetProperty applies opts to the property named key, if it exists.
90139
func (r *Reflected) SetProperty(key string, opts ...PropertyOpt) {
91-
if cv, ok := r.Schema.Properties.Get(key); ok {
140+
if cv, ok := r.Schema.Properties[key]; ok {
92141
for _, opt := range opts {
93142
opt(cv)
94143
}
95144
}
96145
}
97146

147+
// SetOrRemoveProperty applies opts to the property named key when setProperty
148+
// is true, and otherwise removes the property.
98149
func (r *Reflected) SetOrRemoveProperty(key string, setProperty bool, opts ...PropertyOpt) {
99-
if cv, ok := r.Schema.Properties.Get(key); ok {
100-
if setProperty {
101-
for _, opt := range opts {
102-
opt(cv)
103-
}
104-
} else {
105-
r.Schema.Properties.Delete(key)
150+
cv, ok := r.Schema.Properties[key]
151+
if !ok {
152+
return
153+
}
154+
155+
if setProperty {
156+
for _, opt := range opts {
157+
opt(cv)
106158
}
159+
160+
return
107161
}
162+
163+
delete(r.Schema.Properties, key)
108164
}
109165

166+
// RemoveProperty removes the property named key.
110167
func (r *Reflected) RemoveProperty(key string) {
111-
r.Schema.Properties.Delete(key)
168+
delete(r.Schema.Properties, key)
112169
}
113170

171+
// GenOpt configures KCL generation from a [Reflected].
172+
//
173+
// Available options:
174+
// - [Replace]
114175
type GenOpt func(*Reflected)
115176

177+
// Replace is a [GenOpt] that registers a regular-expression replacement applied
178+
// to the generated KCL schema text.
116179
func Replace(re *regexp.Regexp, repl string) GenOpt {
117180
return func(r *Reflected) {
118181
r.replacements = append(r.replacements, replacement{re: re, repl: repl})
119182
}
120183
}
121184

122-
type PropertyOpt func(*invopopjsonschema.Schema)
185+
// PropertyOpt modifies a property [gojsonschema.Schema].
186+
//
187+
// Available options:
188+
// - [WithEnum]
189+
// - [WithItemsEnum]
190+
// - [WithDefault]
191+
// - [WithType]
192+
// - [WithNoContent]
193+
// - [WithAllowAdditionalProperties]
194+
type PropertyOpt func(*gojsonschema.Schema)
123195

196+
// WithEnum is a [PropertyOpt] that sets an enum on a property schema.
124197
func WithEnum(enum []any) PropertyOpt {
125-
return func(s *invopopjsonschema.Schema) {
198+
return func(s *gojsonschema.Schema) {
126199
s.Enum = enum
127200
}
128201
}
129202

130203
// WithItemsEnum is a [PropertyOpt] that sets an enum on an array property's
131204
// items schema.
132205
func WithItemsEnum(enum []any) PropertyOpt {
133-
return func(s *invopopjsonschema.Schema) {
206+
return func(s *gojsonschema.Schema) {
134207
if s.Items != nil {
135208
s.Items.Enum = enum
136209
}
137210
}
138211
}
139212

213+
// WithDefault is a [PropertyOpt] that sets the default on a property schema. A
214+
// nil value leaves the default unset, matching JSON omitempty semantics.
140215
func WithDefault(defaultValue any) PropertyOpt {
141-
return func(s *invopopjsonschema.Schema) {
142-
s.Default = defaultValue
216+
return func(s *gojsonschema.Schema) {
217+
if defaultValue == nil {
218+
return
219+
}
220+
221+
data, err := json.Marshal(defaultValue)
222+
if err != nil {
223+
return
224+
}
225+
226+
s.Default = data
143227
}
144228
}
145229

230+
// WithType is a [PropertyOpt] that sets a single type on a property schema,
231+
// clearing any multi-type union.
146232
func WithType(t string) PropertyOpt {
147-
return func(s *invopopjsonschema.Schema) {
233+
return func(s *gojsonschema.Schema) {
148234
s.Type = t
235+
s.Types = nil
149236
}
150237
}
151238

239+
// WithNoContent is a [PropertyOpt] that removes the items and properties from a
240+
// property schema.
152241
func WithNoContent() PropertyOpt {
153-
return func(s *invopopjsonschema.Schema) {
242+
return func(s *gojsonschema.Schema) {
154243
s.Items = nil
155244
s.Properties = nil
156245
}
157246
}
158247

248+
// WithAllowAdditionalProperties is a [PropertyOpt] that allows additional
249+
// properties on a property schema.
159250
func WithAllowAdditionalProperties() PropertyOpt {
160-
return func(s *invopopjsonschema.Schema) {
161-
s.AdditionalProperties = invopopjsonschema.TrueSchema
251+
return func(s *gojsonschema.Schema) {
252+
s.AdditionalProperties = &gojsonschema.Schema{}
162253
}
163254
}

0 commit comments

Comments
 (0)