From f5a428d054b45f2e7e92ce66bac14d7b4f8673da Mon Sep 17 00:00:00 2001 From: Florian Friedrich Date: Sun, 9 Apr 2023 12:47:27 +0200 Subject: [PATCH] Update to Swift 5.8, remove intermediate existentials protocols --- Package.resolved | 76 +++++++++---------- Package.swift | 2 +- Sources/RouteDocs/DefaultDocsView/docs.leaf | 6 +- Sources/RouteDocs/DocsViewContext.swift | 2 +- Sources/RouteDocs/DocumentationDecoder.swift | 75 ++++++++---------- Sources/RouteDocs/EndpointDocumentation.swift | 2 +- .../Extensions/Application+SetupDocs.swift | 11 +-- .../CustomDocumentable+Stdlib.swift | 2 +- Sources/RouteDocs/TypeDescription.swift | 1 + Sources/RouteDocs/TypeWrapping.swift | 19 +---- 10 files changed, 82 insertions(+), 114 deletions(-) diff --git a/Package.resolved b/Package.resolved index 2c19750..fbafa4f 100644 --- a/Package.resolved +++ b/Package.resolved @@ -5,8 +5,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/swift-server/async-http-client.git", "state" : { - "revision" : "03b3e7b34153299e9b4c4b5c2a6ac790a582a3ac", - "version" : "1.12.0" + "revision" : "864c8d9e0ead5de7ba70b61c8982f89126710863", + "version" : "1.15.0" } }, { @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/vapor/async-kit.git", "state" : { - "revision" : "c3329e444bafbb12d1d312af9191be95348a8175", - "version" : "1.13.0" + "revision" : "9acea4c92f51a5885c149904f0d11db4712dda80", + "version" : "1.16.0" } }, { @@ -23,8 +23,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/vapor/console-kit.git", "state" : { - "revision" : "a7e67a1719933318b5ab7eaaed355cde020465b1", - "version" : "4.5.0" + "revision" : "447f1046fb4e9df40973fe426ecb24a6f0e8d3b4", + "version" : "4.6.0" } }, { @@ -32,8 +32,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/vapor/leaf.git", "state" : { - "revision" : "bf1c27928c3f7f93fcdca570d7514727fa23e14e", - "version" : "4.2.2" + "revision" : "6fe0e843c6599f5189e45c7b08739ebc5c410c3b", + "version" : "4.2.4" } }, { @@ -41,8 +41,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/vapor/leaf-kit.git", "state" : { - "revision" : "c67a1b06561bfb8a427ed7e50e27792e083f2d3e", - "version" : "1.8.0" + "revision" : "13f2fc4c8479113cd23876d9a434ef4573e368bb", + "version" : "1.10.2" } }, { @@ -50,8 +50,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/vapor/multipart-kit.git", "state" : { - "revision" : "0d55c35e788451ee27222783c7d363cb88092fab", - "version" : "4.5.2" + "revision" : "3a31859efeb054cdcd407fe152802ddc5c58bbce", + "version" : "4.5.3" } }, { @@ -59,8 +59,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/vapor/routing-kit.git", "state" : { - "revision" : "ffac7b3a127ce1e85fb232f1a6271164628809ad", - "version" : "4.6.0" + "revision" : "611bc45c5dfb1f54b84d99b89d1f72191fb6b71b", + "version" : "4.7.2" } }, { @@ -77,8 +77,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-atomics.git", "state" : { - "revision" : "919eb1d83e02121cdb434c7bfc1f0c66ef17febe", - "version" : "1.0.2" + "revision" : "6c89474e62719ddcc1e9614989fff2f68208fe10", + "version" : "1.1.0" } }, { @@ -95,8 +95,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-collections.git", "state" : { - "revision" : "f504716c27d2e5d4144fa4794b12129301d17729", - "version" : "1.0.3" + "revision" : "937e904258d22af6e447a0b72c0bc67583ef64a2", + "version" : "1.0.4" } }, { @@ -104,8 +104,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-crypto.git", "state" : { - "revision" : "d9825fa541df64b1a7b182178d61b9a82730d01f", - "version" : "2.1.0" + "revision" : "da0fe44138ab86e380f40a2acbd8a611b07d3f64", + "version" : "2.4.0" } }, { @@ -113,8 +113,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-log.git", "state" : { - "revision" : "6fe203dc33195667ce1759bf0182975e4653ba1c", - "version" : "1.4.4" + "revision" : "32e8d724467f8fe623624570367e3d50c5638e46", + "version" : "1.5.2" } }, { @@ -122,8 +122,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-metrics.git", "state" : { - "revision" : "53be78637ecd165d1ddedc4e20de69b8f43ec3b7", - "version" : "2.3.2" + "revision" : "e8bced74bc6d747745935e469f45d03f048d6cbd", + "version" : "2.3.4" } }, { @@ -131,8 +131,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-nio.git", "state" : { - "revision" : "a16e2f54a25b2af217044e5168997009a505930f", - "version" : "2.42.0" + "revision" : "9b2848d76f5caad08b97e71a04345aa5bdb23a06", + "version" : "2.49.0" } }, { @@ -140,8 +140,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-nio-extras.git", "state" : { - "revision" : "6c84d247754ad77487a6f0694273b89b83efd056", - "version" : "1.14.0" + "revision" : "cc1e5275079380c859417dbea8588531f1a90ec3", + "version" : "1.18.0" } }, { @@ -149,8 +149,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-nio-http2.git", "state" : { - "revision" : "00576e6f1efa5c46dca2ca3081dc56dd233b402d", - "version" : "1.23.0" + "revision" : "38feec96bcd929028939107684073554bf01abeb", + "version" : "1.25.2" } }, { @@ -158,8 +158,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-nio-ssl.git", "state" : { - "revision" : "ba7c0d7f82affc518147ea61d240330bf7f7ea9b", - "version" : "2.22.1" + "revision" : "4fb7ead803e38949eb1d6fabb849206a72c580f3", + "version" : "2.23.0" } }, { @@ -167,8 +167,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-nio-transport-services.git", "state" : { - "revision" : "b6e37a0d442745760d6ed0195d8f283d3ce0414a", - "version" : "1.14.1" + "revision" : "c0d9a144cfaec8d3d596aadde3039286a266c15c", + "version" : "1.15.0" } }, { @@ -185,8 +185,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/vapor/vapor.git", "state" : { - "revision" : "dda0de537e7906414dccd551e77095be1e34e3da", - "version" : "4.65.2" + "revision" : "ba1a308a58ba9d78c5100588d7d8b6e615a98248", + "version" : "4.75.0" } }, { @@ -194,8 +194,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/vapor/websocket-kit.git", "state" : { - "revision" : "2d9d2188a08eef4a869d368daab21b3c08510991", - "version" : "2.6.1" + "revision" : "2b8885974e8d9f522e787805000553f4f7cce8a0", + "version" : "2.7.0" } } ], diff --git a/Package.swift b/Package.swift index aaeaf0a..d70a43a 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.7 +// swift-tools-version:5.8 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription diff --git a/Sources/RouteDocs/DefaultDocsView/docs.leaf b/Sources/RouteDocs/DefaultDocsView/docs.leaf index ee5b57e..452aba3 100644 --- a/Sources/RouteDocs/DefaultDocsView/docs.leaf +++ b/Sources/RouteDocs/DefaultDocsView/docs.leaf @@ -11,9 +11,9 @@ API Docs - + - +
diff --git a/Sources/RouteDocs/DocsViewContext.swift b/Sources/RouteDocs/DocsViewContext.swift index 14138b1..13a7485 100644 --- a/Sources/RouteDocs/DocsViewContext.swift +++ b/Sources/RouteDocs/DocsViewContext.swift @@ -92,7 +92,7 @@ fileprivate extension DocumentationType { } extension EndpointDocumentation { - public var defaultSortOrder: String { method.sortOrder + "/" + path } + public var defaultSortOrder: some Comparable { method.sortOrder + "/" + path } } extension DocsViewContext.Documentation.Object.Body.EnumCase { diff --git a/Sources/RouteDocs/DocumentationDecoder.swift b/Sources/RouteDocs/DocumentationDecoder.swift index 2d56140..fb20e66 100644 --- a/Sources/RouteDocs/DocumentationDecoder.swift +++ b/Sources/RouteDocs/DocumentationDecoder.swift @@ -83,15 +83,19 @@ public struct DocumentationObject: Hashable, CustomStringConvertible, Sendable { let fieldIndention = String(repeating: " ", count: indentionLevel + indentionIncrement) let bodyDesc: String switch body { - case .none: return "\(type)" + case .none: return "\(type)" // unreachable case .fields(let fields): - bodyDesc = fields.sorted { $0.key < $1.key }.map { - "\(fieldIndention)\($0.key): \($0.value.description(indentedBy: indentionLevel + indentionIncrement, indentionIncrement: indentionIncrement))" - }.joined(separator: "\n") + bodyDesc = fields + .sorted { $0.key < $1.key } + .map { [newIndent = indentionLevel + indentionIncrement] in + "\(fieldIndention)\($0.key): \($0.value.description(indentedBy: newIndent, indentionIncrement: indentionIncrement))" + } + .joined(separator: "\n") case .cases(let cases): - bodyDesc = cases.sorted { $0.value < $1.value }.map { - "\(fieldIndention)- \($0.name.map { "\($0): " } ?? "")\($0.value)" - }.joined(separator: "\n") + bodyDesc = cases + .sorted { $0.value < $1.value } + .map { "\(fieldIndention)- \($0.name.map { "\($0): " } ?? "")\($0.value)" } + .joined(separator: "\n") } return """ \(type) { @@ -114,27 +118,18 @@ public protocol CustomDocumentationNamed { static var documentationName: String { get } } -public protocol AnyCustomDocumentable { - static var documentationBody: DocumentationObject.Body { get } - static var anyDocumentationInstance: Any { get } -} - -public protocol CustomDocumentable: AnyCustomDocumentable { +public protocol CustomDocumentable { static var documentationInstance: Self { get } + static var documentationBody: DocumentationObject.Body { get } } extension CustomDocumentable { - @inlinable - public static var anyDocumentationInstance: Any { documentationInstance } -} - -extension AnyCustomDocumentable { static func object(with type: Any.Type) -> DocumentationObject { .init(any: type, body: documentationBody) } } -extension Optional: AnyCustomDocumentable, CustomDocumentable where Wrapped: CustomDocumentable { +extension Optional: CustomDocumentable where Wrapped: CustomDocumentable { public static var documentationBody: DocumentationObject.Body { Wrapped.documentationBody } public static var documentationInstance: Self { .some(Wrapped.documentationInstance) } } @@ -192,9 +187,9 @@ fileprivate struct DocumentationDecoder: Decoder { try storage.setObject(cachedElement.documentation, for: key, at: codingPath) return cachedElement.object as! T } - if let customDocumentable = T.self as? AnyCustomDocumentable.Type { + if let customDocumentable = T.self as? any CustomDocumentable.Type { try storage.setObject(customDocumentable.object(with: type), for: key, at: codingPath) - let result = customDocumentable.anyDocumentationInstance as! T + let result = customDocumentable.documentationInstance as! T try cache(result) return result } @@ -243,19 +238,17 @@ extension DocumentationDecoder { decodedObject = .init(any: type, body: .none) } - func withCodingPath(_ path: Path, do work: (inout DocumentationObject) throws -> T) throws -> T - where Path: Collection, Path.Element == CodingKey - { - func withPath(remainingPath: RemainingPath, - of object: inout DocumentationObject, - do work: (inout DocumentationObject) throws -> T) throws -> T - where RemainingPath: Collection, RemainingPath.Element == Path.Element - { + func withCodingPath(_ path: some Collection, + do work: (inout DocumentationObject) throws -> T) throws -> T { + func withPath(remainingPath: some Collection, + of object: inout DocumentationObject, + do work: (inout DocumentationObject) throws -> T) throws -> T { guard !remainingPath.isEmpty else { return try work(&object) } let nextKey = remainingPath[remainingPath.startIndex] guard var nextObject = object.body.fields?[nextKey.stringValue] else { - throw DecodingError.keyNotFound(nextKey, .init(codingPath: path.dropLast(remainingPath.count), - debugDescription: "No documentation field was decoded (yet) at the given path!")) + throw DecodingError.keyNotFound(nextKey, + .init(codingPath: path.dropLast(remainingPath.count), + debugDescription: "No documentation field was decoded (yet) at the given path!")) } defer { object.body.fields?[nextKey.stringValue] = nextObject } return try withPath(remainingPath: remainingPath.dropFirst(), of: &nextObject, do: work) @@ -284,7 +277,7 @@ extension DocumentationDecoder { fileprivate enum Cache { struct Entry { - let object: Decodable + let object: any Decodable let documentation: DocumentationObject } @@ -347,6 +340,10 @@ extension DocumentationDecoder { var allKeys: Array { guard !decoder.hasPotentialCycle() else { return .init() } + if let caseIterable = Key.self as? any CaseIterable.Type { + let allCases = caseIterable.allCases as any Collection + return allCases as? Array ?? allCases.map({ $0 as! Key }) + } return Key(stringValue: "{any}").map { [$0] } ?? .init() } @@ -472,26 +469,17 @@ extension DocumentationDecoder { self.init(index: intValue) } } -// private struct OpenEndedListKey: CodingKey { -// var intValue: Int? { nil } -// var stringValue: String { "{0...}" } -// -// init?(intValue: Int) { nil } -// init?(stringValue: String) { nil } -// init() {} -// } let decoder: DocumentationDecoder private let builder = TypeBuilder() + private(set) var currentIndex = 0 var codingPath: Array { decoder.codingPath } var count: Int? { nil } var isAtEnd: Bool { currentIndex >= 10 || decoder.hasPotentialCycle() } - private(set) var currentIndex = 0 - private mutating func finalize(with type: T.Type, for key: IndexKey) throws { try decoder.setType(builder.finalizeType(with: type), for: key) } @@ -505,7 +493,8 @@ extension DocumentationDecoder { private func compressTypeFieldsIfNeeded() throws { guard isAtEnd else { return } try decoder.withCurrentCodingPath { - guard let unique = $0.body.fields.map({ Set($0.values) }), unique.count == 1 else { return } + guard let unique = $0.body.fields.map({ Set($0.values) }), unique.count == 1 + else { return } $0.body = .fields(["{0...}": unique[unique.startIndex]]) } } diff --git a/Sources/RouteDocs/EndpointDocumentation.swift b/Sources/RouteDocs/EndpointDocumentation.swift index f12fc4f..183c8f6 100644 --- a/Sources/RouteDocs/EndpointDocumentation.swift +++ b/Sources/RouteDocs/EndpointDocumentation.swift @@ -316,5 +316,5 @@ fileprivate func _openOptionals(in type: Any.Type) -> Any.Type { } fileprivate func _leafType(of type: Any.Type) -> Any.Type { - (type as? AnyTypeWrapping.Type)?.leafType ?? type + (type as? any TypeWrapping.Type)?.leafType ?? type } diff --git a/Sources/RouteDocs/Extensions/Application+SetupDocs.swift b/Sources/RouteDocs/Extensions/Application+SetupDocs.swift index fb1e9b8..b222efa 100644 --- a/Sources/RouteDocs/Extensions/Application+SetupDocs.swift +++ b/Sources/RouteDocs/Extensions/Application+SetupDocs.swift @@ -9,17 +9,8 @@ extension DocsViewContext { } extension ViewRenderer { - @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) public func renderDefaultDocs(with context: DocsViewContext) async throws -> View { - if #available(macOS 12, iOS 15, tvOS 15, watchOS 8, *) { - return try await render("docs", context) - } else { - return try await renderDefaultDocs(with: context).get() - } - } - - public func renderDefaultDocs(with context: DocsViewContext) -> EventLoopFuture { - render("docs", context) + try await render("docs", context) } } diff --git a/Sources/RouteDocs/Extensions/CustomDocumentable+Stdlib.swift b/Sources/RouteDocs/Extensions/CustomDocumentable+Stdlib.swift index e2c1060..e58b43d 100644 --- a/Sources/RouteDocs/Extensions/CustomDocumentable+Stdlib.swift +++ b/Sources/RouteDocs/Extensions/CustomDocumentable+Stdlib.swift @@ -18,7 +18,7 @@ extension UUID: CustomDocumentable { extension URL: CustomDocumentable { @inlinable - public static var documentationInstance: URL { URL(fileURLWithPath: "/") } + public static var documentationInstance: URL { .init(fileURLWithPath: "/") } @inlinable public static var documentationBody: DocumentationObject.Body { .none } diff --git a/Sources/RouteDocs/TypeDescription.swift b/Sources/RouteDocs/TypeDescription.swift index be78896..f1666f0 100644 --- a/Sources/RouteDocs/TypeDescription.swift +++ b/Sources/RouteDocs/TypeDescription.swift @@ -112,6 +112,7 @@ public struct TypeDescription: Hashable, Sendable, Codable, CustomStringConverti } extension TypeDescription { + @frozen public struct NameOptions: OptionSet, Sendable { public typealias RawValue = UInt diff --git a/Sources/RouteDocs/TypeWrapping.swift b/Sources/RouteDocs/TypeWrapping.swift index ba6943c..3602b84 100644 --- a/Sources/RouteDocs/TypeWrapping.swift +++ b/Sources/RouteDocs/TypeWrapping.swift @@ -1,34 +1,21 @@ -public protocol AnyTypeWrapping { - static var wrappedType: Any.Type { get } -} - -public protocol TypeWrapping: AnyTypeWrapping { +public protocol TypeWrapping { associatedtype Wrapped } extension TypeWrapping { - @inlinable - public static var wrappedType: Any.Type { Wrapped.self } -} - -extension AnyTypeWrapping { private static func leafType(history: Array) -> Any.Type { - let wrapped = wrappedType + let wrapped = Wrapped.self guard !history.contains(where: { $0 == wrapped }) else { // If the history contains our wrapped type already, we encountered a loop. // Loops aren't necessarily bad, we just need to detect them // and return the last element (which is us in this case). return self } - return (wrapped as? AnyTypeWrapping.Type)?.leafType(history: history + CollectionOfOne(self)) ?? wrapped + return (wrapped as? any TypeWrapping.Type)?.leafType(history: history + CollectionOfOne(self)) ?? wrapped } internal static var leafType: Any.Type { leafType(history: .init()) } } // We detect optionals seperately, so we need to make them "transparent". -extension Optional: AnyTypeWrapping where Wrapped: AnyTypeWrapping { - @inlinable - public static var wrappedType: Any.Type { Wrapped.wrappedType } -} extension Optional: TypeWrapping where Wrapped: TypeWrapping {}