steipete--codexbar
125 行
5.1 KiB
Swift
125 行
5.1 KiB
Swift
import Foundation
|
|
|
|
/// Parsed view of CommandCode `/internal/billing/credits` + `/internal/billing/subscriptions`.
|
|
public struct CommandCodeUsageSnapshot: Sendable {
|
|
/// USD remaining in the current monthly grant (`credits.monthlyCredits`).
|
|
public let monthlyCreditsRemaining: Double
|
|
/// USD top-up balance carried over (`credits.purchasedCredits`).
|
|
public let purchasedCredits: Double
|
|
/// USD remaining in the premium monthly grant (`credits.premiumMonthlyCredits`).
|
|
public let premiumMonthlyCredits: Double
|
|
/// USD remaining in the open-source monthly grant (`credits.opensourceMonthlyCredits`).
|
|
public let opensourceMonthlyCredits: Double
|
|
/// Subscription plan, or nil when the user is on the free tier.
|
|
public let plan: CommandCodePlanCatalog.Plan?
|
|
/// `currentPeriodEnd` from the active subscription.
|
|
public let billingPeriodEnd: Date?
|
|
/// Subscription status (e.g. `active`, `canceled`).
|
|
public let subscriptionStatus: String?
|
|
/// The optional subscription request timed out or failed for this refresh.
|
|
public let subscriptionEnrichmentUnavailable: Bool
|
|
public let updatedAt: Date
|
|
|
|
public init(
|
|
monthlyCreditsRemaining: Double,
|
|
purchasedCredits: Double,
|
|
premiumMonthlyCredits: Double,
|
|
opensourceMonthlyCredits: Double,
|
|
plan: CommandCodePlanCatalog.Plan?,
|
|
billingPeriodEnd: Date?,
|
|
subscriptionStatus: String?,
|
|
subscriptionEnrichmentUnavailable: Bool = false,
|
|
updatedAt: Date = Date())
|
|
{
|
|
self.monthlyCreditsRemaining = monthlyCreditsRemaining
|
|
self.purchasedCredits = purchasedCredits
|
|
self.premiumMonthlyCredits = premiumMonthlyCredits
|
|
self.opensourceMonthlyCredits = opensourceMonthlyCredits
|
|
self.plan = plan
|
|
self.billingPeriodEnd = billingPeriodEnd
|
|
self.subscriptionStatus = subscriptionStatus
|
|
self.subscriptionEnrichmentUnavailable = subscriptionEnrichmentUnavailable
|
|
self.updatedAt = updatedAt
|
|
}
|
|
|
|
/// USD allocation for the active monthly grant (from the catalog).
|
|
public var monthlyCreditsTotal: Double? {
|
|
self.plan?.monthlyCreditsUSD
|
|
}
|
|
|
|
/// USD spent in the current monthly grant (total – remaining), clamped to [0, total].
|
|
public var monthlyCreditsUsed: Double? {
|
|
guard let total = self.monthlyCreditsTotal else { return nil }
|
|
return max(0, min(total, total - self.monthlyCreditsRemaining))
|
|
}
|
|
|
|
public func toUsageSnapshot() -> UsageSnapshot {
|
|
let primary = self.makePrimaryWindow()
|
|
|
|
let identity = ProviderIdentitySnapshot(
|
|
providerID: .commandcode,
|
|
accountEmail: nil,
|
|
accountOrganization: nil,
|
|
loginMethod: self.makeLoginMethod())
|
|
|
|
return UsageSnapshot(
|
|
primary: primary,
|
|
secondary: nil,
|
|
tertiary: nil,
|
|
providerCost: nil,
|
|
commandCodeSubscriptionEnrichmentUnavailable: self.subscriptionEnrichmentUnavailable,
|
|
commandCodeHasSubscriptionPlan: self.plan != nil,
|
|
commandCodeMonthlyGrantDepleted: self.monthlyCreditsRemaining <= 0,
|
|
updatedAt: self.updatedAt,
|
|
identity: identity)
|
|
}
|
|
|
|
private func makePrimaryWindow() -> RateWindow? {
|
|
guard let total = self.monthlyCreditsTotal, total > 0 else {
|
|
// Free / unknown plan with no allowance — surface 100% so the bar renders empty.
|
|
if self.monthlyCreditsRemaining > 0 || self.purchasedCredits > 0 {
|
|
return RateWindow(
|
|
usedPercent: 0,
|
|
windowMinutes: nil,
|
|
resetsAt: self.billingPeriodEnd,
|
|
resetDescription: nil)
|
|
}
|
|
return nil
|
|
}
|
|
let used = self.monthlyCreditsUsed ?? 0
|
|
let percent = min(100, max(0, (used / total) * 100))
|
|
return RateWindow(
|
|
usedPercent: percent,
|
|
windowMinutes: nil,
|
|
resetsAt: self.billingPeriodEnd,
|
|
resetDescription: nil)
|
|
}
|
|
|
|
private func makeLoginMethod() -> String? {
|
|
var parts: [String] = []
|
|
if let name = self.plan?.displayName, !name.isEmpty {
|
|
parts.append(name)
|
|
}
|
|
if let total = self.monthlyCreditsTotal {
|
|
let used = self.monthlyCreditsUsed ?? 0
|
|
parts.append("\(Self.formatUSD(used)) of \(Self.formatUSD(total))")
|
|
} else if self.monthlyCreditsRemaining > 0 {
|
|
parts.append("\(Self.formatUSD(self.monthlyCreditsRemaining)) remaining")
|
|
}
|
|
if self.purchasedCredits > 0 {
|
|
parts.append("+ \(Self.formatUSD(self.purchasedCredits)) credits")
|
|
}
|
|
return parts.isEmpty ? nil : parts.joined(separator: " · ")
|
|
}
|
|
|
|
static func formatUSD(_ value: Double) -> String {
|
|
let formatter = NumberFormatter()
|
|
formatter.numberStyle = .currency
|
|
formatter.currencyCode = "USD"
|
|
formatter.locale = Locale(identifier: "en_US")
|
|
formatter.maximumFractionDigits = value < 100 ? 2 : 0
|
|
formatter.minimumFractionDigits = value < 100 ? 2 : 0
|
|
return formatter.string(from: NSNumber(value: value)) ?? "$\(value)"
|
|
}
|
|
}
|