@@ -244,32 +244,59 @@ struct TypeMatcher {
244
244
/// - Parameters:
245
245
/// - schema: The schema to check.
246
246
/// - components: The OpenAPI components for looking up references.
247
+ /// - cache: Memoised optionality by reference.
247
248
/// - Throws: An error if there's an issue while checking the schema.
248
249
/// - 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 {
250
251
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
+ }
254
262
}
255
263
256
264
/// Returns a Boolean value indicating whether the schema is optional.
257
265
/// - Parameters:
258
266
/// - schema: The schema to check.
259
267
/// - components: The OpenAPI components for looking up references.
268
+ /// - cache: Memoised optionality by reference.
260
269
/// - Throws: An error if there's an issue while checking the schema.
261
270
/// - 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 {
263
272
guard let schema else {
264
273
// A nil unresolved schema represents a non-optional fragment.
265
274
return false
266
275
}
267
276
switch schema {
268
277
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
272
293
}
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
273
300
}
274
301
275
302
// MARK: - Private
0 commit comments