@@ -244,32 +244,59 @@ struct TypeMatcher {
244244 /// - Parameters:
245245 /// - schema: The schema to check.
246246 /// - components: The OpenAPI components for looking up references.
247+ /// - cache: Memoised optionality by reference.
247248 /// - Throws: An error if there's an issue while checking the schema.
248249 /// - Returns: `true` if the schema is optional, `false` otherwise.
249- func isOptional( _ schema: JSONSchema , components: OpenAPI . Components ) throws -> Bool {
250+ func isOptional( _ schema: JSONSchema , components: OpenAPI . Components , cache : [ JSONReference < JSONSchema > : Bool ] = [ : ] ) throws -> Bool {
250251 if schema. nullable || !schema. required { return true }
251- guard case . reference( let ref, _) = schema. value else { return false }
252- let targetSchema = try components. lookup ( ref)
253- return try isOptional ( targetSchema, components: components)
252+ switch schema. value {
253+ case . null( _) :
254+ return true
255+ case . reference( let ref, _) :
256+ return try isOptional ( ref, components: components, cache: cache)
257+ case . one( of: let schemas, core: _) :
258+ return try schemas. contains ( where: { try isOptional ( $0, components: components, cache: cache) } )
259+ default :
260+ return schema. nullable
261+ }
254262 }
255263
256264 /// Returns a Boolean value indicating whether the schema is optional.
257265 /// - Parameters:
258266 /// - schema: The schema to check.
259267 /// - components: The OpenAPI components for looking up references.
268+ /// - cache: Memoised optionality by reference.
260269 /// - Throws: An error if there's an issue while checking the schema.
261270 /// - Returns: `true` if the schema is optional, `false` otherwise.
262- func isOptional( _ schema: UnresolvedSchema ? , components: OpenAPI . Components ) throws -> Bool {
271+ func isOptional( _ schema: UnresolvedSchema ? , components: OpenAPI . Components , cache : [ JSONReference < JSONSchema > : Bool ] = [ : ] ) throws -> Bool {
263272 guard let schema else {
264273 // A nil unresolved schema represents a non-optional fragment.
265274 return false
266275 }
267276 switch schema {
268277 case . a( let ref) :
269- let targetSchema = try components. lookup ( ref)
270- return try isOptional ( targetSchema, components: components)
271- case . b( let schema) : return try isOptional ( schema, components: components)
278+ return try isOptional ( ref. jsonReference, components: components, cache: cache)
279+ case . b( let schema) : return try isOptional ( schema, components: components, cache: cache)
280+ }
281+ }
282+
283+ /// Returns a Boolean value indicating whether the referenced schema is optional.
284+ /// - Parameters:
285+ /// - schema: The reference to check.
286+ /// - components: The OpenAPI components for looking up references.
287+ /// - cache: Memoised optionality by reference.
288+ /// - Throws: An error if there's an issue while checking the schema.
289+ /// - Returns: `true` if the schema is optional, `false` otherwise.
290+ func isOptional( _ ref: JSONReference < JSONSchema > , components: OpenAPI . Components , cache: [ JSONReference < JSONSchema > : Bool ] = [ : ] ) throws -> Bool {
291+ if let result = cache [ ref] {
292+ return result
272293 }
294+ let targetSchema = try components. lookup ( ref)
295+ var cache = cache
296+ cache [ ref] = false // Pre-cache to treat directly recursive types as non-nullable.
297+ let result = try isOptional ( targetSchema, components: components, cache: cache)
298+ cache [ ref] = result
299+ return result
273300 }
274301
275302 // MARK: - Private
0 commit comments