Skip to content

Commit 460082c

Browse files
giginetclaude
andcommitted
refactor: unify BuildOperationMetrics into a single struct with counters
Instead of using an enum with separate CacheMetrics and CounterMetrics associated values, use a single struct with counters/taskCounters fields. The legacy Xcode 15.3 - Xcode 26.3 cache format (clangCacheHits, etc.) is translated into the unified counter format during deserialization. Signed-off-by: Kohki Miki <kohki.miki@lycorp.co.jp> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent f2b5447 commit 460082c

File tree

2 files changed

+38
-47
lines changed

2 files changed

+38
-47
lines changed

Sources/XCLogParser/activityparser/IDEActivityModel.swift

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -774,41 +774,39 @@ public class IDEActivityLogSectionAttachment: Encodable {
774774
}
775775

776776
/// Build operation metrics whose JSON schema differs by Xcode version.
777-
public enum BuildOperationMetrics: Encodable {
778-
/// Xcode 15.3 - Xcode 16.x format
779-
case v15_3(CacheMetrics)
780-
/// Xcode 26.4+ format
781-
case v26_4(CounterMetrics)
782-
783-
public struct CacheMetrics: Codable {
784-
public let clangCacheHits: Int
785-
public let clangCacheMisses: Int
786-
public let swiftCacheHits: Int
787-
public let swiftCacheMisses: Int
788-
}
777+
public struct BuildOperationMetrics: Codable {
778+
public let counters: [String: Int]
779+
public let taskCounters: [String: [String: Int]]
789780

790-
public struct CounterMetrics: Codable {
791-
public let counters: [String: Int]
792-
public let taskCounters: [String: [String: Int]]
781+
public init(counters: [String: Int], taskCounters: [String: [String: Int]]) {
782+
self.counters = counters
783+
self.taskCounters = taskCounters
793784
}
794785

786+
/// Parses BuildOperationMetrics from JSON data, translating the legacy
787+
/// Xcode 15.3 - Xcode 26.3 cache format into the unified counter format.
795788
public init(from jsonData: Data) throws {
796789
let decoder = JSONDecoder()
797-
if let cache = try? decoder.decode(CacheMetrics.self, from: jsonData) {
798-
self = .v15_3(cache)
790+
if let direct = try? decoder.decode(BuildOperationMetrics.self, from: jsonData) {
791+
self = direct
799792
} else {
800-
let counter = try decoder.decode(CounterMetrics.self, from: jsonData)
801-
self = .v26_4(counter)
793+
let legacy = try decoder.decode(LegacyCacheMetrics.self, from: jsonData)
794+
self.counters = [
795+
"clangCacheHits": legacy.clangCacheHits,
796+
"clangCacheMisses": legacy.clangCacheMisses,
797+
"swiftCacheHits": legacy.swiftCacheHits,
798+
"swiftCacheMisses": legacy.swiftCacheMisses,
799+
]
800+
self.taskCounters = [:]
802801
}
803802
}
804803

805-
public func encode(to encoder: Encoder) throws {
806-
switch self {
807-
case .v15_3(let metrics):
808-
try metrics.encode(to: encoder)
809-
case .v26_4(let metrics):
810-
try metrics.encode(to: encoder)
811-
}
804+
/// Legacy Xcode 15.3 - Xcode 26.3 format, used only for deserialization.
805+
private struct LegacyCacheMetrics: Decodable {
806+
let clangCacheHits: Int
807+
let clangCacheMisses: Int
808+
let swiftCacheHits: Int
809+
let swiftCacheMisses: Int
812810
}
813811
}
814812
}

Tests/XCLogParserTests/ActivityParserTests.swift

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -420,10 +420,10 @@ class ActivityParserTests: XCTestCase {
420420
XCTAssertEqual(3, logSection.attachments.count)
421421
XCTAssertEqual(logSection.attachments[0].backtrace?.frames.first?.category, .ruleNeverBuilt)
422422
print(logSection.attachments)
423-
if case .some(.v15_3(let cache)) = logSection.attachments[1].buildOperationMetrics {
424-
XCTAssertEqual(cache.clangCacheMisses, 2)
423+
if let metrics = logSection.attachments[1].buildOperationMetrics {
424+
XCTAssertEqual(metrics.counters["clangCacheMisses"], 2)
425425
} else {
426-
XCTFail("Expected v15_3 BuildOperationMetrics")
426+
XCTFail("Expected BuildOperationMetrics")
427427
}
428428
XCTAssertEqual(logSection.attachments[2].metrics?.wcDuration, 1)
429429
XCTAssertEqual(0, logSection.unknown)
@@ -510,11 +510,11 @@ class ActivityParserTests: XCTestCase {
510510
XCTAssertEqual("localizedResultString", logSection.localizedResultString)
511511
XCTAssertEqual("xcbuildSignature", logSection.xcbuildSignature)
512512
XCTAssertEqual(1, logSection.attachments.count)
513-
if case .some(.v26_4(let counter)) = logSection.attachments[0].buildOperationMetrics {
514-
XCTAssertEqual(counter.counters, [:])
515-
XCTAssertEqual(counter.taskCounters["SwiftDriver"]?["moduleDependenciesNotValidatedTasks"], 1)
513+
if let metrics = logSection.attachments[0].buildOperationMetrics {
514+
XCTAssertEqual(metrics.counters, [:])
515+
XCTAssertEqual(metrics.taskCounters["SwiftDriver"]?["moduleDependenciesNotValidatedTasks"], 1)
516516
} else {
517-
XCTFail("Expected v26_4 BuildOperationMetrics")
517+
XCTFail("Expected BuildOperationMetrics")
518518
}
519519
XCTAssertEqual(42, logSection.unknown)
520520
}
@@ -640,26 +640,19 @@ class ActivityParserTests: XCTestCase {
640640
let json = #"{"clangCacheHits":1,"clangCacheMisses":2,"swiftCacheHits":3,"swiftCacheMisses":4}"#
641641
let data = json.data(using: .utf8)!
642642
let metrics = try IDEActivityLogSectionAttachment.BuildOperationMetrics(from: data)
643-
if case .v15_3(let cache) = metrics {
644-
XCTAssertEqual(cache.clangCacheHits, 1)
645-
XCTAssertEqual(cache.clangCacheMisses, 2)
646-
XCTAssertEqual(cache.swiftCacheHits, 3)
647-
XCTAssertEqual(cache.swiftCacheMisses, 4)
648-
} else {
649-
XCTFail("Expected v15_3 BuildOperationMetrics")
650-
}
643+
XCTAssertEqual(metrics.counters["clangCacheHits"], 1)
644+
XCTAssertEqual(metrics.counters["clangCacheMisses"], 2)
645+
XCTAssertEqual(metrics.counters["swiftCacheHits"], 3)
646+
XCTAssertEqual(metrics.counters["swiftCacheMisses"], 4)
647+
XCTAssertEqual(metrics.taskCounters, [:])
651648
}
652649

653650
func testBuildOperationMetricsWithCounterFormat() throws {
654651
let json = #"{"counters":{"a":1},"taskCounters":{"SwiftDriver":{"x":2}}}"#
655652
let data = json.data(using: .utf8)!
656653
let metrics = try IDEActivityLogSectionAttachment.BuildOperationMetrics(from: data)
657-
if case .v26_4(let counter) = metrics {
658-
XCTAssertEqual(counter.counters["a"], 1)
659-
XCTAssertEqual(counter.taskCounters["SwiftDriver"]?["x"], 2)
660-
} else {
661-
XCTFail("Expected v26_4 BuildOperationMetrics")
662-
}
654+
XCTAssertEqual(metrics.counters["a"], 1)
655+
XCTAssertEqual(metrics.taskCounters["SwiftDriver"]?["x"], 2)
663656
}
664657

665658
}

0 commit comments

Comments
 (0)