import Foundation enum MetricValue: Sendable, Equatable { case percent(Double) case integer(Int) case currency(Double) case text(String) case date(Date) var displayString: String { switch self { case .percent(let value): return String(format: "%.0f%%", value) case .integer(let value): return NumberFormatter.localizedString(from: NSNumber(value: value), number: .decimal) case .currency(let value): return String(format: "$%.2f", value) case .text(let value): return value case .date(let value): return Self.dateFormatter.string(from: value) } } var compactString: String { menuBarCompactString } var menuBarCompactString: String { switch self { case .percent(let value): return String(format: "%.0f%%", value) case .integer(let value): if value >= 1_000_000 { return String(format: "%.1fM", Double(value) / 1_000_000) } if value >= 1_000 { return String(format: "%.1fK", Double(value) / 1_000) } return "\(value)" case .currency(let value): return String(format: "$%.0f", value) case .text(let value): if value.count > 10 { return String(value.prefix(10)) + "…" } return value case .date(let value): return Self.shortDateFormatter.string(from: value) } } private static let dateFormatter: DateFormatter = { let formatter = DateFormatter() formatter.dateStyle = .medium formatter.timeStyle = .short return formatter }() private static let shortDateFormatter: DateFormatter = { let formatter = DateFormatter() formatter.dateStyle = .short formatter.timeStyle = .short return formatter }() } struct ProviderUsageSnapshot: Sendable { let providerID: ProviderID let fetchedAt: Date let values: [UsageMetric: MetricValue] let isStale: Bool func value(for metric: UsageMetric) -> MetricValue? { values[metric] } func formatted(_ metric: UsageMetric, compact: Bool = false) -> String? { guard let value = value(for: metric) else { return nil } return compact ? value.menuBarCompactString : value.displayString } func menuBarDisplay(for metrics: [UsageMetric]) -> String? { let parts = metrics.compactMap { formatted($0, compact: true) } guard !parts.isEmpty else { return nil } return parts.joined(separator: "·") } func firstPercent(in metrics: [UsageMetric]) -> Double? { for metric in metrics { if case .percent(let value)? = value(for: metric) { return value } } return nil } } struct ProviderDisplayState: Sendable { let providerID: ProviderID let snapshot: ProviderUsageSnapshot? let errorMessage: String? let isLoading: Bool static func loading(_ providerID: ProviderID) -> ProviderDisplayState { ProviderDisplayState(providerID: providerID, snapshot: nil, errorMessage: nil, isLoading: true) } static func success(_ snapshot: ProviderUsageSnapshot) -> ProviderDisplayState { ProviderDisplayState( providerID: snapshot.providerID, snapshot: snapshot, errorMessage: nil, isLoading: false ) } static func failure(_ providerID: ProviderID, message: String) -> ProviderDisplayState { ProviderDisplayState(providerID: providerID, snapshot: nil, errorMessage: message, isLoading: false) } }