import Foundation // swiftformat:disable:next redundantSendable struct AntigravityQuotaSummary: Sendable, Equatable { let description: String? let groups: [AntigravityQuotaSummaryGroup] } // swiftformat:disable:next redundantSendable struct AntigravityQuotaSummaryGroup: Sendable, Equatable { let displayName: String let description: String? let buckets: [AntigravityQuotaSummaryBucket] } // swiftformat:disable:next redundantSendable struct AntigravityQuotaSummaryBucket: Sendable, Equatable { let bucketId: String let displayName: String let remainingFraction: Double? let resetTime: Date? let resetDescription: String? let disabled: Bool init( bucketId: String, displayName: String, remainingFraction: Double?, resetTime: Date? = nil, resetDescription: String?, disabled: Bool) { self.bucketId = bucketId self.displayName = displayName self.remainingFraction = remainingFraction self.resetTime = resetTime self.resetDescription = resetDescription self.disabled = disabled } } extension AntigravityStatusProbe { static func parseQuotaSummaryResponse(_ data: Data) throws -> AntigravityStatusSnapshot { let decoder = JSONDecoder() let response = try decoder.decode(QuotaSummaryResponse.self, from: data) if let invalid = Self.invalidCode(response.code) { throw AntigravityStatusProbeError.apiError(invalid) } let payload = response.response ?? response.summary ?? response.rootPayload guard let payload else { throw AntigravityStatusProbeError.parseFailed("Missing quota summary") } let groups = payload.groups.compactMap(self.quotaSummaryGroup(from:)) guard !groups.isEmpty else { throw AntigravityStatusProbeError.parseFailed("Missing quota groups") } return AntigravityStatusSnapshot( quotaSummary: AntigravityQuotaSummary( description: payload.description, groups: groups), accountEmail: nil, accountPlan: nil, source: .local) } private static func quotaSummaryGroup(from payload: QuotaSummaryGroupPayload) -> AntigravityQuotaSummaryGroup? { let displayName = payload.displayName?.trimmingCharacters(in: .whitespacesAndNewlines) let buckets = (payload.buckets ?? []).compactMap(self.quotaSummaryBucket(from:)) guard !buckets.isEmpty else { return nil } return AntigravityQuotaSummaryGroup( displayName: self.nonEmpty(displayName) ?? "Quota", description: payload.description, buckets: buckets) } private static func quotaSummaryBucket(from payload: QuotaSummaryBucketPayload) -> AntigravityQuotaSummaryBucket? { let bucketId = payload.bucketId?.trimmingCharacters(in: .whitespacesAndNewlines) let displayName = payload.displayName?.trimmingCharacters(in: .whitespacesAndNewlines) guard let resolvedBucketId = bucketId, !resolvedBucketId.isEmpty else { return nil } let resetTime = payload.resetTime.flatMap { Self.parseDate($0) } return AntigravityQuotaSummaryBucket( bucketId: resolvedBucketId, displayName: self.nonEmpty(displayName) ?? resolvedBucketId, remainingFraction: payload.resolvedRemainingFraction, resetTime: resetTime, resetDescription: payload.description, disabled: payload.disabled ?? false) } private static func nonEmpty(_ value: String?) -> String? { guard let value, !value.isEmpty else { return nil } return value } } private struct QuotaSummaryResponse: Decodable { let code: CodeValue? let message: String? let response: QuotaSummaryPayload? let summary: QuotaSummaryPayload? let description: String? let groups: [QuotaSummaryGroupPayload]? var rootPayload: QuotaSummaryPayload? { guard let groups else { return nil } return QuotaSummaryPayload(description: self.description, groups: groups) } } private struct QuotaSummaryPayload: Decodable { let description: String? let groups: [QuotaSummaryGroupPayload] init(description: String?, groups: [QuotaSummaryGroupPayload]) { self.description = description self.groups = groups } private enum CodingKeys: String, CodingKey { case description case groups } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.description = try container.decodeIfPresent(String.self, forKey: .description) self.groups = try container.decodeIfPresent([QuotaSummaryGroupPayload].self, forKey: .groups) ?? [] } } private struct QuotaSummaryGroupPayload: Decodable { let displayName: String? let description: String? let buckets: [QuotaSummaryBucketPayload]? } private struct QuotaSummaryBucketPayload: Decodable { let bucketId: String? let displayName: String? let description: String? let disabled: Bool? let remainingFraction: Double? let remaining: QuotaSummaryRemainingPayload? let resetTime: String? var resolvedRemainingFraction: Double? { self.remainingFraction ?? self.remaining?.remainingFraction } } private struct QuotaSummaryRemainingPayload: Decodable { let remainingFraction: Double? private enum CodingKeys: String, CodingKey { case remainingFraction case oneofCase = "case" case value } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) if let remainingFraction = try container.decodeIfPresent(Double.self, forKey: .remainingFraction) { self.remainingFraction = remainingFraction return } let oneofCase = try container.decodeIfPresent(String.self, forKey: .oneofCase) if oneofCase == "remainingFraction" { self.remainingFraction = try container.decodeIfPresent(Double.self, forKey: .value) } else { self.remainingFraction = nil } } }