@@ -111,30 +111,68 @@ func (jenny Schema) objectToDefinition(object ast.Object) Definition {
111111}
112112
113113func (jenny Schema ) formatType (typeDef ast.Type ) Definition {
114+ var definition Definition
114115 switch typeDef .Kind {
115116 case ast .KindStruct :
116- return jenny .formatStruct (typeDef )
117+ definition = jenny .formatStruct (typeDef )
117118 case ast .KindScalar :
118- return jenny .formatScalar (typeDef )
119+ definition = jenny .formatScalar (typeDef )
119120 case ast .KindRef :
120- return jenny .formatRef (typeDef )
121+ definition = jenny .formatRef (typeDef )
121122 case ast .KindEnum :
122- return jenny .formatEnum (typeDef )
123+ definition = jenny .formatEnum (typeDef )
123124 case ast .KindArray :
124- return jenny .formatArray (typeDef )
125+ definition = jenny .formatArray (typeDef )
125126 case ast .KindMap :
126- return jenny .formatMap (typeDef )
127+ definition = jenny .formatMap (typeDef )
127128 case ast .KindDisjunction :
128- return jenny .formatDisjunction (typeDef )
129+ definition = jenny .formatDisjunction (typeDef )
129130 case ast .KindIntersection :
130- return jenny .formatIntersection (typeDef )
131+ definition = jenny .formatIntersection (typeDef )
131132 case ast .KindComposableSlot :
132- return jenny .formatComposableSlot ()
133+ definition = jenny .formatComposableSlot ()
133134 case ast .KindConstantRef :
134- return jenny .formatConstantRef (typeDef )
135+ definition = jenny .formatConstantRef (typeDef )
136+ default :
137+ definition = orderedmap .New [string , any ]()
135138 }
136139
137- return orderedmap .New [string , any ]()
140+ if typeDef .Nullable {
141+ definition = jenny .applyNullable (typeDef .Kind , definition )
142+ }
143+
144+ return definition
145+ }
146+
147+ // applyNullable wraps a definition to express "type | null".
148+ // Ref-like kinds need special treatment in OpenAPI 3.0 because $ref replaces the whole object
149+ // and cannot be combined with other keywords — those use allOf as a wrapper.
150+ // All other kinds can carry nullable: true inline (OpenAPI 3.0) or use anyOf (JSON Schema).
151+ func (jenny Schema ) applyNullable (kind ast.Kind , definition Definition ) Definition {
152+ nullDef := orderedmap .New [string , any ]()
153+ nullDef .Set ("type" , "null" )
154+
155+ refLike := kind == ast .KindRef || kind == ast .KindConstantRef
156+
157+ if jenny .OpenAPI3Compatible {
158+ nullable := orderedmap .New [string , any ]()
159+ if refLike {
160+ nullable .Set ("nullable" , true )
161+ nullable .Set ("allOf" , []Definition {definition })
162+ } else {
163+ // Copy the existing definition and add nullable: true inline
164+ definition .Iterate (func (key string , value any ) {
165+ nullable .Set (key , value )
166+ })
167+ nullable .Set ("nullable" , true )
168+ }
169+ return nullable
170+ }
171+
172+ // JSON Schema: anyOf: [{...}, {type: "null"}]
173+ wrapper := orderedmap .New [string , any ]()
174+ wrapper .Set ("anyOf" , []Definition {definition , nullDef })
175+ return wrapper
138176}
139177
140178func (jenny Schema ) formatScalar (typeDef ast.Type ) Definition {
0 commit comments