1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
|
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
@propertyWrapper
final class HeapBox<T> {
var wrappedValue: T
init(wrappedValue: T) {
self.wrappedValue = wrappedValue
}
}
/// JSON Schema representation for version draft-07
/// https://json-schema.org/draft-07/draft-handrews-json-schema-01
///
/// NOTE: draft-07 is the latest version of JSON Schema that is supported by
/// most of the tools. We may need to update this schema in the future.
struct JSONSchema: Encodable {
enum CodingKeys: String, CodingKey {
case _schema = "$schema"
case id = "$id"
case comment = "$comment"
case title
case type
case description
case properties
case required
case `enum`
case items
case additionalProperties
case markdownDescription
case markdownEnumDescriptions
}
var _schema: String?
var id: String?
var comment: String?
var title: String?
var type: String?
var description: String?
var properties: [String: JSONSchema]?
var required: [String]?
var `enum`: [String]?
@HeapBox
var items: JSONSchema?
@HeapBox
var additionalProperties: JSONSchema?
/// VSCode extension: Markdown formatted description for rich hover
/// https://github.com/microsoft/vscode-wiki/blob/main/Setting-Descriptions.md
var markdownDescription: String?
/// VSCode extension: Markdown formatted descriptions for rich hover for enum values
/// https://github.com/microsoft/vscode-wiki/blob/main/Setting-Descriptions.md
var markdownEnumDescriptions: [String]?
func encode(to encoder: any Encoder) throws {
// Manually implement encoding to use `encodeIfPresent` for HeapBox-ed fields
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encodeIfPresent(_schema, forKey: ._schema)
try container.encodeIfPresent(id, forKey: .id)
try container.encodeIfPresent(comment, forKey: .comment)
try container.encodeIfPresent(title, forKey: .title)
try container.encodeIfPresent(type, forKey: .type)
try container.encodeIfPresent(description, forKey: .description)
if let properties = properties, !properties.isEmpty {
try container.encode(properties, forKey: .properties)
}
if let required = required, !required.isEmpty {
try container.encode(required, forKey: .required)
}
try container.encodeIfPresent(`enum`, forKey: .enum)
try container.encodeIfPresent(items, forKey: .items)
try container.encodeIfPresent(additionalProperties, forKey: .additionalProperties)
try container.encodeIfPresent(markdownDescription, forKey: .markdownDescription)
if let markdownEnumDescriptions {
try container.encode(markdownEnumDescriptions, forKey: .markdownEnumDescriptions)
}
}
}
struct JSONSchemaBuilder {
let context: OptionSchemaContext
func build(from typeSchema: OptionTypeSchama) throws -> JSONSchema {
var schema = try buildJSONSchema(from: typeSchema)
schema._schema = "http://json-schema.org/draft-07/schema#"
return schema
}
private func buildJSONSchema(from typeSchema: OptionTypeSchama) throws -> JSONSchema {
var schema = JSONSchema()
switch typeSchema.kind {
case .boolean: schema.type = "boolean"
case .integer: schema.type = "integer"
case .number: schema.type = "number"
case .string: schema.type = "string"
case .array(value: let value):
schema.type = "array"
schema.items = try buildJSONSchema(from: value)
case .dictionary(value: let value):
schema.type = "object"
schema.additionalProperties = try buildJSONSchema(from: value)
case .struct(let structInfo):
schema.type = "object"
var properties: [String: JSONSchema] = [:]
var required: [String] = []
for property in structInfo.properties {
let propertyType = property.type
var propertySchema = try buildJSONSchema(from: propertyType)
propertySchema.description = property.description
// As we usually use Markdown syntax for doc comments, set `markdownDescription`
// too for better rendering in VSCode.
propertySchema.markdownDescription = property.description
properties[property.name] = propertySchema
if !propertyType.isOptional {
required.append(property.name)
}
}
schema.properties = properties
schema.required = required
case .enum(let enumInfo):
schema.type = "string"
schema.enum = enumInfo.cases.map(\.name)
// Set `markdownEnumDescriptions` for better rendering in VSCode rich hover
// Unlike `description`, `enumDescriptions` field is not a part of JSON Schema spec,
// so we only set `markdownEnumDescriptions` here.
if enumInfo.cases.contains(where: { $0.description != nil }) {
schema.markdownEnumDescriptions = enumInfo.cases.map { $0.description ?? "" }
}
}
return schema
}
}
|