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

180 lines
5.2 KiB
Swift

import Foundation
public enum ClaudePlan: String, CaseIterable, Sendable {
case max
case pro
case team
case enterprise
case ultra
public var brandedLoginMethod: String {
switch self {
case .max:
"Claude Max"
case .pro:
"Claude Pro"
case .team:
"Claude Team"
case .enterprise:
"Claude Enterprise"
case .ultra:
"Claude Ultra"
}
}
/// Branded label including the Max usage multiplier when the rate-limit tier carries one.
public func brandedLoginMethod(rateLimitTier: String?) -> String {
guard self == .max, let multiplier = Self.maxUsageMultiplier(rateLimitTier: rateLimitTier) else {
return self.brandedLoginMethod
}
return "\(self.brandedLoginMethod) \(multiplier)"
}
public var compactLoginMethod: String {
switch self {
case .max:
"Max"
case .pro:
"Pro"
case .team:
"Team"
case .enterprise:
"Enterprise"
case .ultra:
"Ultra"
}
}
public var countsAsSubscription: Bool {
switch self {
case .max, .pro, .team, .ultra:
true
case .enterprise:
false
}
}
public static func fromOAuthRateLimitTier(_ rateLimitTier: String?) -> Self? {
self.fromRateLimitTier(rateLimitTier)
}
public static func fromOAuthCredentials(subscriptionType: String?, rateLimitTier: String?) -> Self? {
self.fromCompatibilityLoginMethod(subscriptionType)
?? self.fromOAuthRateLimitTier(rateLimitTier)
}
public static func fromWebAccount(rateLimitTier: String?, billingType: String?) -> Self? {
if let plan = self.fromRateLimitTier(rateLimitTier) {
return plan
}
let tier = Self.normalized(rateLimitTier)
let billing = Self.normalized(billingType)
if billing.contains("stripe"), tier.contains("claude") {
return .pro
}
return nil
}
public static func fromCompatibilityLoginMethod(_ loginMethod: String?) -> Self? {
let words = Self.normalizedWords(loginMethod)
if words.isEmpty {
return nil
}
if words.contains("max") {
return .max
}
if words.contains("pro") {
return .pro
}
if words.contains("team") {
return .team
}
if words.contains("enterprise") {
return .enterprise
}
if words.contains("ultra") {
return .ultra
}
return nil
}
public static func oauthLoginMethod(rateLimitTier: String?) -> String? {
self.fromOAuthRateLimitTier(rateLimitTier)?.brandedLoginMethod(rateLimitTier: rateLimitTier)
}
public static func oauthLoginMethod(subscriptionType: String?, rateLimitTier: String?) -> String? {
self.fromOAuthCredentials(
subscriptionType: subscriptionType,
rateLimitTier: rateLimitTier)?.brandedLoginMethod(rateLimitTier: rateLimitTier)
}
public static func webLoginMethod(rateLimitTier: String?, billingType: String?) -> String? {
self.fromWebAccount(
rateLimitTier: rateLimitTier,
billingType: billingType)?.brandedLoginMethod(rateLimitTier: rateLimitTier)
}
public static func cliCompatibilityLoginMethod(_ loginMethod: String?) -> String? {
guard let loginMethod = loginMethod?.trimmingCharacters(in: .whitespacesAndNewlines),
!loginMethod.isEmpty
else {
return nil
}
if let plan = self.fromCompatibilityLoginMethod(loginMethod) {
return plan.compactLoginMethod
}
return loginMethod
}
public static func isSubscriptionLoginMethod(_ loginMethod: String?) -> Bool {
self.fromCompatibilityLoginMethod(loginMethod)?.countsAsSubscription ?? false
}
private static func fromRateLimitTier(_ rateLimitTier: String?) -> Self? {
let tier = Self.normalized(rateLimitTier)
if tier.contains("max") {
return .max
}
if tier.contains("pro") {
return .pro
}
if tier.contains("team") {
return .team
}
if tier.contains("enterprise") {
return .enterprise
}
return nil
}
private static func maxUsageMultiplier(rateLimitTier: String?) -> String? {
let words = Self.normalizedWords(rateLimitTier)
guard let maxIndex = words.firstIndex(of: Self.max.rawValue),
words.indices.contains(maxIndex + 1)
else {
return nil
}
let multiplier = words[maxIndex + 1]
guard multiplier.last == "x", Int(multiplier.dropLast()) != nil else {
return nil
}
return multiplier
}
private static func normalized(_ text: String?) -> String {
text?
.trimmingCharacters(in: .whitespacesAndNewlines)
.lowercased() ?? ""
}
private static func normalizedWords(_ text: String?) -> [String] {
self.normalized(text)
.split(whereSeparator: { !$0.isLetter && !$0.isNumber })
.map(String.init)
}
}