Skip to content

Commit 879eb58

Browse files
committed
Progress Reporter Protocol
1 parent 090a75d commit 879eb58

File tree

5 files changed

+244
-144
lines changed

5 files changed

+244
-144
lines changed

Sources/Swiftly/Install.swift

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import Foundation
44
import SwiftlyCore
55
import SystemPackage
66
@preconcurrency import TSCBasic
7-
import TSCUtility
87

98
struct Install: SwiftlyCommand {
109
public static let configuration = CommandConfiguration(
@@ -313,16 +312,16 @@ struct Install: SwiftlyCommand {
313312
}
314313
}
315314

316-
let animation: ProgressAnimationProtocol =
315+
let animation: ProgressReporterProtocol =
317316
if let progressFile
318317
{
319318
try JsonFileProgressReporter(ctx, filePath: progressFile)
320319
} else {
321-
PercentProgressAnimation(stream: stdoutStream, header: "Downloading \(version)")
320+
ConsoleProgressReporter(stream: stdoutStream, header: "Downloading \(version)")
322321
}
323322

324323
defer {
325-
try? (animation as? JsonFileProgressReporter)?.close()
324+
try? animation.close()
326325
}
327326

328327
var lastUpdate = Date()
@@ -351,7 +350,7 @@ struct Install: SwiftlyCommand {
351350

352351
lastUpdate = Date()
353352

354-
animation.update(
353+
await animation.update(
355354
step: progress.receivedBytes,
356355
total: progress.totalBytes!,
357356
text:
@@ -363,10 +362,10 @@ struct Install: SwiftlyCommand {
363362
throw SwiftlyError(
364363
message: "\(version) does not exist at URL \(notFound.url), exiting")
365364
} catch {
366-
animation.complete(success: false)
365+
await animation.complete(success: false)
367366
throw error
368367
}
369-
animation.complete(success: true)
368+
await animation.complete(success: true)
370369

371370
if verifySignature {
372371
try await Swiftly.currentPlatform.verifyToolchainSignature(

Sources/Swiftly/JsonFileProgressReporter.swift

Lines changed: 0 additions & 62 deletions
This file was deleted.
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import Foundation
2+
import SwiftlyCore
3+
import SystemPackage
4+
import TSCBasic
5+
import TSCUtility
6+
7+
public protocol ProgressReporterProtocol {
8+
/// Updates the progress animation with the current step, total steps, and an optional text message.
9+
func update(step: Int, total: Int, text: String) async
10+
11+
/// Completes the progress animation, indicating success or failure.
12+
func complete(success: Bool) async
13+
14+
/// Closes any resources used by the reporter, if applicable.
15+
func close() throws
16+
}
17+
18+
/// Progress reporter that delegates to a `PercentProgressAnimation` for console output.
19+
struct ConsoleProgressReporter: ProgressReporterProtocol {
20+
private let reporter: PercentProgressAnimation
21+
22+
init(stream: WritableByteStream, header: String) {
23+
self.reporter = PercentProgressAnimation(stream: stream, header: header)
24+
}
25+
26+
func update(step: Int, total: Int, text: String) async {
27+
self.reporter.update(step: step, total: total, text: text)
28+
}
29+
30+
func complete(success: Bool) async {
31+
self.reporter.complete(success: success)
32+
}
33+
34+
func close() {
35+
// No resources to close for console reporter
36+
}
37+
}
38+
39+
enum ProgressInfo: Codable {
40+
case step(timestamp: Date, percent: Int, text: String)
41+
case complete(success: Bool)
42+
}
43+
44+
struct JsonFileProgressReporter: ProgressReporterProtocol {
45+
let filePath: FilePath
46+
private let encoder: JSONEncoder
47+
private let ctx: SwiftlyCoreContext
48+
private let fileHandle: FileHandle
49+
50+
init(_ ctx: SwiftlyCoreContext, filePath: FilePath, encoder: JSONEncoder = JSONEncoder()) throws
51+
{
52+
self.ctx = ctx
53+
self.filePath = filePath
54+
self.encoder = encoder
55+
self.fileHandle = try FileHandle(forWritingTo: URL(fileURLWithPath: filePath.string))
56+
}
57+
58+
private func writeProgress(_ progress: ProgressInfo) async {
59+
let jsonData = try? self.encoder.encode(progress)
60+
guard let jsonData = jsonData else {
61+
await self.ctx.message("Failed to encode progress entry to JSON")
62+
return
63+
}
64+
65+
self.fileHandle.write(jsonData)
66+
self.fileHandle.write("\n".data(using: .utf8) ?? Data())
67+
try? self.fileHandle.synchronize()
68+
}
69+
70+
func update(step: Int, total: Int, text: String) async {
71+
guard total > 0 && step <= total else {
72+
return
73+
}
74+
await self.writeProgress(
75+
ProgressInfo.step(
76+
timestamp: Date(),
77+
percent: Int(Double(step) / Double(total) * 100),
78+
text: text
79+
)
80+
)
81+
}
82+
83+
func complete(success: Bool) async {
84+
await self.writeProgress(ProgressInfo.complete(success: success))
85+
}
86+
87+
func close() throws {
88+
try self.fileHandle.close()
89+
}
90+
}

0 commit comments

Comments
 (0)