From a5f06e09ce75ef0fd1e45c8e38043b93b11c033c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steffen=20K=C3=B6tte?= Date: Sat, 9 Oct 2021 17:12:15 -0700 Subject: [PATCH 1/2] Adjust for Swift 5.5 output --- .../PackageDescription.swift | 19 +++++++++++++-- .../PackageDescriptionTest.swift | 23 ++++++++++--------- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/Sources/SwiftDependencyUpdaterLibrary/PackageDescription.swift b/Sources/SwiftDependencyUpdaterLibrary/PackageDescription.swift index 2555743..4047984 100644 --- a/Sources/SwiftDependencyUpdaterLibrary/PackageDescription.swift +++ b/Sources/SwiftDependencyUpdaterLibrary/PackageDescription.swift @@ -3,6 +3,13 @@ import Releases import ShellOut struct PackageDependency: Decodable { + + enum CodingKeys: String, CodingKey { + case name = "identity" + case requirement + case url = "location" + } + let name: String let requirement: DependencyRequirement let url: URL @@ -69,7 +76,15 @@ enum PackageDescriptionError: Error, Equatable { } struct PackageDescription: Decodable { - let dependencies: [PackageDependency] + + enum CodingKeys: String, CodingKey { + case dependencyMap = "dependencies" + } + + let dependencyMap: [[String: [PackageDependency]]] + var dependencies: [PackageDependency] { + dependencyMap.flatMap { $0.values.flatMap { $0 } } + } static func loadPackageDescription(from folder: URL) throws -> Self { let json = try readPackageDescription(from: folder) @@ -79,7 +94,7 @@ struct PackageDescription: Decodable { let packageDescription = try decoder.decode(Self.self, from: data) return packageDescription } catch { - throw PackageDescriptionError.parsingFailed(error.localizedDescription, json) + throw PackageDescriptionError.parsingFailed(String(describing: error), json) } } diff --git a/Tests/SwiftDependencyUpdaterLibraryTests/PackageDescriptionTest.swift b/Tests/SwiftDependencyUpdaterLibraryTests/PackageDescriptionTest.swift index d4b0e7c..22f88c3 100644 --- a/Tests/SwiftDependencyUpdaterLibraryTests/PackageDescriptionTest.swift +++ b/Tests/SwiftDependencyUpdaterLibraryTests/PackageDescriptionTest.swift @@ -13,20 +13,21 @@ class PackageDescriptionTest: XCTestCase { } func testInvalidFile() { + var caughtError: Error? let folder = emptyFolderURL() let file = temporaryFileURL(in: folder, name: "Package.swift") createFile(at: file, content: "// swift-tools-version:5.4.0\n") - #if os(Linux) - assert( - try PackageDescription.loadPackageDescription(from: folder), - throws: PackageDescriptionError.loadingFailed("\(folder.path): error: malformed") - ) - #else - assert( - try PackageDescription.loadPackageDescription(from: folder), - throws: PackageDescriptionError.loadingFailed("/private\(folder.path): error: malformed") - ) - #endif + + XCTAssertThrowsError(try PackageDescription.loadPackageDescription(from: folder)) { + caughtError = $0 + } + + guard let error = caughtError as? PackageDescriptionError, case let .loadingFailed(description) = error else { + XCTFail("Unexpected error, got \(type(of: caughtError!)) \(caughtError!)) instead of PackageDescriptionError.loadingFailed") + return + } + + XCTAssert(description.contains("\(folder.path): error: malformed")) } func testParsing() { From 4beaa3a4a98d3cc31eb5135e5577df7e89f7327f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steffen=20K=C3=B6tte?= Date: Sun, 17 Oct 2021 17:47:58 -0700 Subject: [PATCH 2/2] Allow Swift 5.4 output in parallel to Swift 5.5 --- .../Dependency.swift | 2 +- .../PackageDescription.swift | 50 +++++++++++++++++-- .../PackageDescriptionTest.swift | 6 +-- 3 files changed, 49 insertions(+), 9 deletions(-) diff --git a/Sources/SwiftDependencyUpdaterLibrary/Dependency.swift b/Sources/SwiftDependencyUpdaterLibrary/Dependency.swift index 4f208f4..9413890 100644 --- a/Sources/SwiftDependencyUpdaterLibrary/Dependency.swift +++ b/Sources/SwiftDependencyUpdaterLibrary/Dependency.swift @@ -30,7 +30,7 @@ struct Dependency { } static func loadDependencies(from folder: URL) throws -> [Dependency] { - let packageDescription = try PackageDescription.loadPackageDescription(from: folder) + let packageDescription = try PackageDescriptionFactory.loadPackageDescription(from: folder) if packageDescription.dependencies.isEmpty { return [] } diff --git a/Sources/SwiftDependencyUpdaterLibrary/PackageDescription.swift b/Sources/SwiftDependencyUpdaterLibrary/PackageDescription.swift index 4047984..b9d0d2f 100644 --- a/Sources/SwiftDependencyUpdaterLibrary/PackageDescription.swift +++ b/Sources/SwiftDependencyUpdaterLibrary/PackageDescription.swift @@ -2,7 +2,22 @@ import Foundation import Releases import ShellOut -struct PackageDependency: Decodable { +protocol PackageDependency: Decodable { + + var name: String { get } + var requirement: DependencyRequirement { get } + var url: URL { get } + +} + +private struct PackageDependencyV54: PackageDependency { + + let name: String + let requirement: DependencyRequirement + let url: URL +} + +private struct PackageDependencyV55: PackageDependency { enum CodingKeys: String, CodingKey { case name = "identity" @@ -75,25 +90,46 @@ enum PackageDescriptionError: Error, Equatable { case parsingFailed(String, String) } -struct PackageDescription: Decodable { +struct PackageDescriptionV54: PackageDescription { + + enum CodingKeys: String, CodingKey { + case dependenciesArray = "dependencies" + } + + private let dependenciesArray: [PackageDependencyV54] + var dependencies: [PackageDependency] { + dependenciesArray + } + +} + +struct PackageDescriptionV55: PackageDescription { enum CodingKeys: String, CodingKey { case dependencyMap = "dependencies" } - let dependencyMap: [[String: [PackageDependency]]] + private let dependencyMap: [[String: [PackageDependencyV55]]] var dependencies: [PackageDependency] { dependencyMap.flatMap { $0.values.flatMap { $0 } } } - static func loadPackageDescription(from folder: URL) throws -> Self { +} + +enum PackageDescriptionFactory { + static func loadPackageDescription(from folder: URL) throws -> PackageDescription { let json = try readPackageDescription(from: folder) let data = json.data(using: .utf8)! let decoder = JSONDecoder() do { - let packageDescription = try decoder.decode(Self.self, from: data) + let packageDescription = try decoder.decode(PackageDescriptionV55.self, from: data) return packageDescription } catch { + do { + let packageDescription = try decoder.decode(PackageDescriptionV54.self, from: data) + return packageDescription + } catch { + } throw PackageDescriptionError.parsingFailed(String(describing: error), json) } } @@ -109,6 +145,10 @@ struct PackageDescription: Decodable { } } +protocol PackageDescription: Decodable { + var dependencies: [PackageDependency] { get } +} + extension PackageDescriptionError: LocalizedError { public var errorDescription: String? { switch self { diff --git a/Tests/SwiftDependencyUpdaterLibraryTests/PackageDescriptionTest.swift b/Tests/SwiftDependencyUpdaterLibraryTests/PackageDescriptionTest.swift index 22f88c3..2b73991 100644 --- a/Tests/SwiftDependencyUpdaterLibraryTests/PackageDescriptionTest.swift +++ b/Tests/SwiftDependencyUpdaterLibraryTests/PackageDescriptionTest.swift @@ -7,7 +7,7 @@ class PackageDescriptionTest: XCTestCase { func testEmptyFolder() { let folder = emptyFolderURL() assert( - try PackageDescription.loadPackageDescription(from: folder), + try PackageDescriptionFactory.loadPackageDescription(from: folder), throws: PackageDescriptionError.loadingFailed("error: root manifest not found") ) } @@ -18,7 +18,7 @@ class PackageDescriptionTest: XCTestCase { let file = temporaryFileURL(in: folder, name: "Package.swift") createFile(at: file, content: "// swift-tools-version:5.4.0\n") - XCTAssertThrowsError(try PackageDescription.loadPackageDescription(from: folder)) { + XCTAssertThrowsError(try PackageDescriptionFactory.loadPackageDescription(from: folder)) { caughtError = $0 } @@ -34,7 +34,7 @@ class PackageDescriptionTest: XCTestCase { let folder = emptyFolderURL() let file = temporaryFileURL(in: folder, name: "Package.swift") createFile(at: file, content: TestUtils.packageSwiftFileContent) - let result = try! PackageDescription.loadPackageDescription(from: folder) + let result = try! PackageDescriptionFactory.loadPackageDescription(from: folder) XCTAssertEqual(result.dependencies.count, 8) XCTAssertEqual(result.dependencies[0].name, "a")