@@ -59,6 +59,16 @@ enum ToolchainSource: Codable, CustomStringConvertible {
5959 }
6060}
6161
62+ private enum ToolchainVersionCodingKeys: String, CodingKey {
63+ case name
64+ case type
65+ case branch
66+ case major
67+ case minor
68+ case patch
69+ case date
70+ }
71+
6272struct AvailableToolchainInfo: OutputData {
6373 let version: ToolchainVersion
6474 let inUse: Bool
@@ -82,24 +92,14 @@ struct AvailableToolchainInfo: OutputData {
8292 private enum CodingKeys: String, CodingKey {
8393 case version
8494 case inUse
85- case `default`
95+ case isDefault
8696 case installed
8797 }
8898
89- private enum ToolchainVersionCodingKeys: String, CodingKey {
90- case name
91- case type
92- case branch
93- case major
94- case minor
95- case patch
96- case date
97- }
98-
9999 public func encode(to encoder: Encoder) throws {
100100 var container = encoder.container(keyedBy: CodingKeys.self)
101101 try container.encode(self.inUse, forKey: .inUse)
102- try container.encode(self.isDefault, forKey: .default )
102+ try container.encode(self.isDefault, forKey: .isDefault )
103103 try container.encode(self.installed, forKey: .installed)
104104
105105 // Encode the version as a object
@@ -131,7 +131,7 @@ struct AvailableToolchainInfo: OutputData {
131131
132132struct AvailableToolchainsListInfo: OutputData {
133133 let toolchains: [AvailableToolchainInfo]
134- let selector: ToolchainSelector?
134+ var selector: ToolchainSelector?
135135
136136 init(toolchains: [AvailableToolchainInfo], selector: ToolchainSelector? = nil) {
137137 self.toolchains = toolchains
@@ -175,3 +175,167 @@ struct AvailableToolchainsListInfo: OutputData {
175175 return lines.joined(separator: "\n")
176176 }
177177}
178+
179+ struct InstallToolchainInfo: OutputData {
180+ let version: ToolchainVersion
181+ let inUse: Bool
182+ let isDefault: Bool
183+
184+ init(version: ToolchainVersion, inUse: Bool, isDefault: Bool) {
185+ self.version = version
186+ self.inUse = inUse
187+ self.isDefault = isDefault
188+ }
189+
190+ var description: String {
191+ var message = "\(version)"
192+
193+ if self.inUse {
194+ message += " (in use)"
195+ }
196+ if self.isDefault {
197+ message += " (default)"
198+ }
199+ return message
200+ }
201+
202+ private enum CodingKeys: String, CodingKey {
203+ case version
204+ case inUse
205+ case isDefault
206+ }
207+
208+ public func encode(to encoder: Encoder) throws {
209+ var container = encoder.container(keyedBy: CodingKeys.self)
210+ try container.encode(self.inUse, forKey: .inUse)
211+ try container.encode(self.isDefault, forKey: .isDefault)
212+
213+ // Encode the version as a object
214+ var versionContainer = container.nestedContainer(
215+ keyedBy: ToolchainVersionCodingKeys.self, forKey: .version
216+ )
217+ try versionContainer.encode(self.version.name, forKey: .name)
218+
219+ switch self.version {
220+ case let .stable(release):
221+ try versionContainer.encode("stable", forKey: .type)
222+ try versionContainer.encode(release.major, forKey: .major)
223+ try versionContainer.encode(release.minor, forKey: .minor)
224+ try versionContainer.encode(release.patch, forKey: .patch)
225+ case let .snapshot(snapshot):
226+ try versionContainer.encode("snapshot", forKey: .type)
227+ try versionContainer.encode(snapshot.date, forKey: .date)
228+ try versionContainer.encode(snapshot.branch.name, forKey: .branch)
229+
230+ if let major = snapshot.branch.major,
231+ let minor = snapshot.branch.minor
232+ {
233+ try versionContainer.encode(major, forKey: .major)
234+ try versionContainer.encode(minor, forKey: .minor)
235+ }
236+ }
237+ }
238+
239+ init(from decoder: Decoder) throws {
240+ let container = try decoder.container(keyedBy: CodingKeys.self)
241+ self.inUse = try container.decode(Bool.self, forKey: .inUse)
242+ self.isDefault = try container.decode(Bool.self, forKey: .isDefault)
243+
244+ // Decode the version as a object
245+ let versionContainer = try container.nestedContainer(
246+ keyedBy: ToolchainVersionCodingKeys.self, forKey: .version
247+ )
248+ let name = try versionContainer.decode(String.self, forKey: .name)
249+
250+ switch try versionContainer.decode(String.self, forKey: .type) {
251+ case "stable":
252+ let major = try versionContainer.decode(Int.self, forKey: .major)
253+ let minor = try versionContainer.decode(Int.self, forKey: .minor)
254+ let patch = try versionContainer.decode(Int.self, forKey: .patch)
255+ self.version = .stable(
256+ ToolchainVersion.StableRelease(major: major, minor: minor, patch: patch))
257+ case "snapshot":
258+ let date = try versionContainer.decode(String.self, forKey: .date)
259+ let branchName = try versionContainer.decode(String.self, forKey: .branch)
260+ let branchMajor = try? versionContainer.decodeIfPresent(Int.self, forKey: .major)
261+ let branchMinor = try? versionContainer.decodeIfPresent(Int.self, forKey: .minor)
262+
263+ // Determine the branch from the decoded data
264+ let branch: ToolchainVersion.Snapshot.Branch
265+ if branchName == "main" {
266+ branch = .main
267+ } else if let major = branchMajor, let minor = branchMinor {
268+ branch = .release(major: major, minor: minor)
269+ } else {
270+ throw DecodingError.dataCorruptedError(
271+ forKey: ToolchainVersionCodingKeys.branch,
272+ in: versionContainer,
273+ debugDescription: "Invalid branch format: \(branchName)"
274+ )
275+ }
276+
277+ self.version = .snapshot(
278+ ToolchainVersion.Snapshot(
279+ branch: branch,
280+ date: date
281+ ))
282+ default:
283+ throw DecodingError.dataCorruptedError(
284+ forKey: ToolchainVersionCodingKeys.type,
285+ in: versionContainer,
286+ debugDescription: "Unknown toolchain type"
287+ )
288+ }
289+ }
290+ }
291+
292+ struct InstalledToolchainsListInfo: OutputData {
293+ let toolchains: [InstallToolchainInfo]
294+ var selector: ToolchainSelector?
295+
296+ private enum CodingKeys: String, CodingKey {
297+ case toolchains
298+ }
299+
300+ var description: String {
301+ var lines: [String] = []
302+
303+ if let selector = selector {
304+ let modifier =
305+ switch selector
306+ {
307+ case let .stable(major, minor, nil):
308+ if let minor {
309+ "Swift \(major).\(minor) release"
310+ } else {
311+ "Swift \(major) release"
312+ }
313+ case .snapshot(.main, nil):
314+ "main development snapshot"
315+ case let .snapshot(.release(major, minor), nil):
316+ "\(major).\(minor) development snapshot"
317+ default:
318+ "matching"
319+ }
320+
321+ let header = "Installed \(modifier) toolchains"
322+ lines.append(header)
323+ lines.append(String(repeating: "-", count: header.count))
324+ lines.append(contentsOf: self.toolchains.map(\.description))
325+ } else {
326+ let releaseToolchains = self.toolchains.filter { $0.version.isStableRelease() }
327+ let snapshotToolchains = self.toolchains.filter { $0.version.isSnapshot() }
328+
329+ lines.append("Installed release toolchains")
330+ lines.append("----------------------------")
331+ lines.append(contentsOf: releaseToolchains.map(\.description))
332+
333+ lines.append("")
334+ lines.append("Installed snapshot toolchains")
335+ lines.append("-----------------------------")
336+ lines.append(contentsOf: snapshotToolchains.map(\.description))
337+ }
338+
339+ return lines.joined(separator: "\n")
340+ }
341+ }
0 commit comments