chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
import Foundation
|
||||
|
||||
/// A malformed trace line, with enough context to find and fix it.
|
||||
public struct AdaptiveRefreshTraceParseError: Error, Sendable, Equatable, CustomStringConvertible {
|
||||
public let lineNumber: Int
|
||||
public let content: String
|
||||
public let underlyingDescription: String
|
||||
|
||||
public init(lineNumber: Int, content: String, underlyingDescription: String) {
|
||||
self.lineNumber = lineNumber
|
||||
self.content = content
|
||||
self.underlyingDescription = underlyingDescription
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
"trace line \(self.lineNumber) is malformed: \(self.underlyingDescription) (content: \(self.content))"
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses newline-delimited JSON adaptive-refresh traces.
|
||||
///
|
||||
/// Deliberate choice: a malformed line **fails the whole parse** rather than being silently
|
||||
/// skipped. A trace is acceptance evidence — if a line is corrupt (truncated write, disk-full
|
||||
/// mid-append, hand-edited fixture with a typo), the honest answer is "this trace is untrustworthy
|
||||
/// as a whole", not "here are metrics computed from however much of it happened to parse". A
|
||||
/// silently-shortened trace would still produce a superficially plausible replay report, which is
|
||||
/// worse than a loud failure: it hides exactly the kind of gap that would bias staleness/refresh
|
||||
/// counts. Callers that genuinely want best-effort parsing can catch the error and fall back to
|
||||
/// `AdaptiveRefreshTraceParser.parseTolerantly`, which skips bad lines and returns what parsed.
|
||||
public enum AdaptiveRefreshTraceParser {
|
||||
public static func parse(_ text: String) throws -> [AdaptiveRefreshTraceRecord] {
|
||||
let decoder = Self.makeDecoder()
|
||||
var records: [AdaptiveRefreshTraceRecord] = []
|
||||
for (index, line) in text.split(
|
||||
omittingEmptySubsequences: false,
|
||||
whereSeparator: \.isNewline).enumerated()
|
||||
{
|
||||
let trimmed = line.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !trimmed.isEmpty else { continue }
|
||||
guard let data = trimmed.data(using: .utf8) else {
|
||||
throw AdaptiveRefreshTraceParseError(
|
||||
lineNumber: index + 1,
|
||||
content: trimmed,
|
||||
underlyingDescription: "not valid UTF-8")
|
||||
}
|
||||
do {
|
||||
try records.append(decoder.decode(AdaptiveRefreshTraceRecord.self, from: data))
|
||||
} catch {
|
||||
throw AdaptiveRefreshTraceParseError(
|
||||
lineNumber: index + 1,
|
||||
content: trimmed,
|
||||
underlyingDescription: String(describing: error))
|
||||
}
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
public static func parse(contentsOf url: URL) throws -> [AdaptiveRefreshTraceRecord] {
|
||||
let text = try String(contentsOf: url, encoding: .utf8)
|
||||
return try self.parse(text)
|
||||
}
|
||||
|
||||
/// Best-effort variant: skips lines that fail to parse instead of throwing. Not the default —
|
||||
/// see the type-level documentation for why silent skipping is the wrong default for
|
||||
/// acceptance-evidence traces. Exists for callers (future exploratory tooling) that explicitly
|
||||
/// want partial data over none.
|
||||
public static func parseTolerantly(_ text: String) -> [AdaptiveRefreshTraceRecord] {
|
||||
let decoder = Self.makeDecoder()
|
||||
var records: [AdaptiveRefreshTraceRecord] = []
|
||||
for line in text.split(omittingEmptySubsequences: false, whereSeparator: \.isNewline) {
|
||||
let trimmed = line.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
guard !trimmed.isEmpty, let data = trimmed.data(using: .utf8) else { continue }
|
||||
if let record = try? decoder.decode(AdaptiveRefreshTraceRecord.self, from: data) {
|
||||
records.append(record)
|
||||
}
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
private static func makeDecoder() -> JSONDecoder {
|
||||
let decoder = JSONDecoder()
|
||||
decoder.dateDecodingStrategy = .iso8601
|
||||
return decoder
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user