Skip to content

Commit d0dd9db

Browse files
committed
Support decoding v1 and v2
1 parent c4807e9 commit d0dd9db

File tree

9 files changed

+324
-57
lines changed

9 files changed

+324
-57
lines changed
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// Copyright 2022 Dave Verwer, Sven A. Schmidt, and other contributors.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import Foundation
16+
17+
18+
enum PackageResolved {
19+
case v1(V1)
20+
case v2(V2)
21+
22+
// object:
23+
// pins:
24+
// - package: String
25+
// repositoryURL: URL
26+
// state:
27+
// branch: String?
28+
// revision: CommitHash
29+
// version: SemVer?
30+
// - ...
31+
// version: 1
32+
struct V1: Decodable, Equatable {
33+
var object: Object
34+
35+
struct Object: Decodable, Equatable {
36+
var pins: [Pin]
37+
38+
struct Pin: Decodable, Equatable {
39+
var package: String
40+
var repositoryURL: URL
41+
}
42+
}
43+
}
44+
45+
// object:
46+
// pins:
47+
// - identity: String
48+
// location: URL
49+
// state:
50+
// revision: CommitHash
51+
// version: SemVer?
52+
// - ...
53+
// version: 2
54+
struct V2: Decodable, Equatable {
55+
var pins: [Pin]
56+
57+
struct Pin: Decodable, Equatable {
58+
var identity: String
59+
var location: URL
60+
}
61+
}
62+
}
63+
64+
65+
extension PackageResolved {
66+
var v1: V1? {
67+
switch self {
68+
case let .v1(value):
69+
return value
70+
case .v2:
71+
return nil
72+
}
73+
}
74+
75+
var v2: V2? {
76+
switch self {
77+
case .v1:
78+
return nil
79+
case let .v2(value):
80+
return value
81+
}
82+
}
83+
}
84+
85+
86+
extension PackageResolved: Decodable {
87+
init(from decoder: Decoder) throws {
88+
if let value = try? decoder.singleValueContainer().decode(V1.self) {
89+
self = .v1(value)
90+
return
91+
}
92+
if let value = try? decoder.singleValueContainer().decode(V2.self) {
93+
self = .v2(value)
94+
return
95+
}
96+
97+
throw DecodingError(message: "failed to decode PackageResolved")
98+
}
99+
}
100+
101+
102+
struct DecodingError: Error {
103+
var message: String
104+
}

Sources/ReleaseNotesCore/Parser.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ enum Parser {
6060
Prefix { $0 != newPackageToken && $0 != updatedRevisionToken }
6161
}
6262

63-
static let newPackage = Parse { Update(packageName: $0, oldRevision: nil) } with: {
63+
static let newPackage = Parse { Update(packageId: $0, oldRevision: nil) } with: {
6464
Skip {
6565
upToStart
6666
"\(newPackageToken) "
@@ -71,7 +71,7 @@ enum Parser {
7171
}
7272
}
7373

74-
static let updatedRevision = Parse(Update.init(packageName:oldRevision:)) {
74+
static let updatedRevision = Parse(Update.init(packageId:oldRevision:)) {
7575
Skip {
7676
upToStart
7777
"\(updatedRevisionToken) "

Sources/ReleaseNotesCore/ReleaseNotes.swift

Lines changed: 49 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import ArgumentParser
1616
import Foundation
1717

1818

19-
typealias PackageName = String
19+
typealias PackageId = String
2020

2121

2222
struct ReleaseNotes: AsyncParsableCommand {
@@ -25,7 +25,7 @@ struct ReleaseNotes: AsyncParsableCommand {
2525
var workingDirecory: String = "."
2626

2727
func runAsync() async throws {
28-
guard let packageMap = getPackageMap(at: workingDirecory) else {
28+
guard let packageMap = Self.getPackageMap(at: workingDirecory) else {
2929
print("Failed to parse Package.resolved in \(workingDirecory).")
3030
return
3131
}
@@ -51,9 +51,9 @@ struct ReleaseNotes: AsyncParsableCommand {
5151

5252
print("\nRelease notes URLs (updating from):")
5353
for update in updates {
54-
let releasesURL = packageMap[update.packageName]
54+
let releasesURL = packageMap[caseIgnoring: update.packageId]
5555
.map { $0.absoluteString.droppingGitExtension + "/releases" }
56-
?? "\(update.packageName)"
56+
?? "\(update.packageId)"
5757
print(releasesURL, "(\(update.oldRevision?.description ?? "new package"))")
5858
}
5959
}
@@ -90,30 +90,40 @@ struct ReleaseNotes: AsyncParsableCommand {
9090
return process
9191
}
9292

93-
func getPackageMap(at path: String) -> [PackageName: URL]? {
94-
// object:
95-
// pins:
96-
// - package: String
97-
// repositoryURL: URL
98-
// state:
99-
// branch: String?
100-
// revision: CommitHash
101-
// version: SemVer?
102-
// - ...
103-
// version: 1
104-
struct PackageResolved: Decodable {
105-
var object: Object
106-
107-
struct Object: Decodable {
108-
var pins: [Pin]
109-
110-
struct Pin: Decodable {
111-
var package: String
112-
var repositoryURL: URL
113-
}
114-
}
115-
}
116-
93+
// static func decodePackageResolved(at url: URL) -> [PackageId: URL]? {
94+
// // object:
95+
// // pins:
96+
// // - package: String
97+
// // repositoryURL: URL
98+
// // state:
99+
// // branch: String?
100+
// // revision: CommitHash
101+
// // version: SemVer?
102+
// // - ...
103+
// // version: 1
104+
// struct PackageResolvedV1: Decodable {
105+
// var object: Object
106+
//
107+
// struct Object: Decodable {
108+
// var pins: [Pin]
109+
//
110+
// struct Pin: Decodable {
111+
// var package: String
112+
// var repositoryURL: URL
113+
// }
114+
// }
115+
// }
116+
//
117+
// guard FileManager.default.fileExists(atPath: url.path),
118+
// let json = FileManager.default.contents(atPath: url.path),
119+
// let packageResolved = try? JSONDecoder()
120+
// .decode(PackageResolvedV1.self, from: json)
121+
// else {
122+
// return nil
123+
// }
124+
// }
125+
126+
static func getPackageMap(at path: String) -> [PackageId: URL]? {
117127
let filePath = URL(fileURLWithPath: path)
118128
.appendingPathComponent("Package.resolved").path
119129
guard FileManager.default.fileExists(atPath: filePath),
@@ -124,9 +134,17 @@ struct ReleaseNotes: AsyncParsableCommand {
124134
return nil
125135
}
126136

127-
return Dictionary(packageResolved.object.pins
128-
.map { ($0.package, $0.repositoryURL) },
129-
uniquingKeysWith: { first, _ in first })
137+
// return Dictionary(packageResolved.object.pins
138+
// .map { ($0.package, $0.repositoryURL) },
139+
// uniquingKeysWith: { first, _ in first })
140+
return [:]
130141
}
131142

132143
}
144+
145+
146+
private extension [PackageId: URL] {
147+
subscript(caseIgnoring packageId: PackageId) -> URL? {
148+
first(where: { $0.key.lowercased() == packageId.lowercased() })?.value
149+
}
150+
}

Sources/ReleaseNotesCore/Update.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@
1313
// limitations under the License.
1414

1515
struct Update: CustomStringConvertible, Equatable {
16-
var packageName: PackageName
16+
var packageId: PackageId
1717
var oldRevision: Revision?
1818

1919
var description: String {
2020
if let oldRevision = oldRevision {
21-
return "\(packageName) @ \(oldRevision)"
21+
return "\(packageId) @ \(oldRevision)"
2222
} else {
23-
return "\(packageName) (new package)"
23+
return "\(packageId) (new package)"
2424
}
2525
}
2626
}

Tests/ReleaseNotesTests/Fixtures/Package.resolved-v1.json

Lines changed: 35 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Tests/ReleaseNotesTests/Fixtures/Package.resolved-v2.json

Lines changed: 50 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright 2022 Dave Verwer, Sven A. Schmidt, and other contributors.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
@testable import ReleaseNotesCore
16+
import XCTest
17+
18+
19+
final class ReleaseNotesCoreTests: XCTestCase {
20+
21+
func test_getPackageMap_v1() throws {
22+
let path = fixtureUrl(for: "Package.resolved-v1.json").path
23+
let map = ReleaseNotes.getPackageMap(at: path)
24+
25+
XCTAssertEqual(map?.count, 3)
26+
}
27+
28+
}

0 commit comments

Comments
 (0)