@@ -247,10 +247,29 @@ struct TypeMatcher {
247247 /// - Throws: An error if there's an issue while checking the schema.
248248 /// - Returns: `true` if the schema is optional, `false` otherwise.
249249 func isOptional( _ schema: JSONSchema , components: OpenAPI . Components ) throws -> Bool {
250+ var cache = [ JSONReference < JSONSchema > : Bool ] ( )
251+ return try isOptional ( schema, components: components, cache: & cache)
252+ }
253+
254+ /// Returns a Boolean value indicating whether the schema is optional.
255+ /// - Parameters:
256+ /// - schema: The schema to check.
257+ /// - components: The OpenAPI components for looking up references.
258+ /// - cache: Memoised optionality by reference.
259+ /// - Throws: An error if there's an issue while checking the schema.
260+ /// - Returns: `true` if the schema is optional, `false` otherwise.
261+ func isOptional( _ schema: JSONSchema , components: OpenAPI . Components , cache: inout [ JSONReference < JSONSchema > : Bool ] ) throws -> Bool {
250262 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)
263+ switch schema. value {
264+ case . null( _) :
265+ return true
266+ case . reference( let ref, _) :
267+ return try isOptional ( ref, components: components, cache: & cache)
268+ case . one( of: let schemas, core: _) :
269+ return try schemas. contains ( where: { try isOptional ( $0, components: components, cache: & cache) } )
270+ default :
271+ return schema. nullable
272+ }
254273 }
255274
256275 /// Returns a Boolean value indicating whether the schema is optional.
@@ -260,16 +279,56 @@ struct TypeMatcher {
260279 /// - Throws: An error if there's an issue while checking the schema.
261280 /// - Returns: `true` if the schema is optional, `false` otherwise.
262281 func isOptional( _ schema: UnresolvedSchema ? , components: OpenAPI . Components ) throws -> Bool {
282+ var cache = [ JSONReference < JSONSchema > : Bool ] ( )
283+ return try isOptional ( schema, components: components, cache: & cache)
284+ }
285+
286+ /// Returns a Boolean value indicating whether the schema is optional.
287+ /// - Parameters:
288+ /// - schema: The schema to check.
289+ /// - components: The OpenAPI components for looking up references.
290+ /// - cache: Memoised optionality by reference.
291+ /// - Throws: An error if there's an issue while checking the schema.
292+ /// - Returns: `true` if the schema is optional, `false` otherwise.
293+ func isOptional( _ schema: UnresolvedSchema ? , components: OpenAPI . Components , cache: inout [ JSONReference < JSONSchema > : Bool ] ) throws -> Bool {
263294 guard let schema else {
264295 // A nil unresolved schema represents a non-optional fragment.
265296 return false
266297 }
267298 switch schema {
268299 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)
300+ return try isOptional ( ref. jsonReference, components: components, cache: & cache)
301+ case . b( let schema) : return try isOptional ( schema, components: components, cache: & cache)
302+ }
303+ }
304+
305+ /// Returns a Boolean value indicating whether the referenced schema is optional.
306+ /// - Parameters:
307+ /// - schema: The reference to check.
308+ /// - components: The OpenAPI components for looking up references.
309+ /// - Throws: An error if there's an issue while checking the schema.
310+ /// - Returns: `true` if the schema is optional, `false` otherwise.
311+ func isOptional( _ ref: JSONReference < JSONSchema > , components: OpenAPI . Components ) throws -> Bool {
312+ var cache = [ JSONReference < JSONSchema > : Bool ] ( )
313+ return try isOptional ( ref, components: components, cache: & cache)
314+ }
315+
316+ /// Returns a Boolean value indicating whether the referenced schema is optional.
317+ /// - Parameters:
318+ /// - schema: The reference to check.
319+ /// - components: The OpenAPI components for looking up references.
320+ /// - cache: Memoised optionality by reference.
321+ /// - Throws: An error if there's an issue while checking the schema.
322+ /// - Returns: `true` if the schema is optional, `false` otherwise.
323+ func isOptional( _ ref: JSONReference < JSONSchema > , components: OpenAPI . Components , cache: inout [ JSONReference < JSONSchema > : Bool ] ) throws -> Bool {
324+ if let result = cache [ ref] {
325+ return result
272326 }
327+ let targetSchema = try components. lookup ( ref)
328+ cache [ ref] = false // Pre-cache to treat directly recursive types as non-nullable.
329+ let result = try isOptional ( targetSchema, components: components, cache: & cache)
330+ cache [ ref] = result
331+ return result
273332 }
274333
275334 // MARK: - Private
0 commit comments