Files
2026-07-13 12:22:33 +08:00

174 lines
6.1 KiB
Swift

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
}
}
}