diff --git a/src/account.rs b/src/account.rs index 186007b..9312d69 100644 --- a/src/account.rs +++ b/src/account.rs @@ -121,7 +121,7 @@ fn print_accounts(store: &Store, json: bool) -> Result<()> { &account.id, &account.email, auth_file::account_auth_mode_name(account), - account.plan_type.as_deref().unwrap_or("-"), + account_plan_display(account), &primary_quota, &secondary_quota, ); @@ -633,6 +633,14 @@ fn format_quota(account: &Account) -> String { .unwrap_or_else(|| "-".to_string()) } +fn account_plan_display(account: &Account) -> &str { + if account.requires_reauth { + "reauth" + } else { + account.plan_type.as_deref().unwrap_or("-") + } +} + fn format_quota_cells(account: &Account) -> (String, String) { let Some(quota) = account.quota.as_ref() else { return ("-".to_string(), "-".to_string()); diff --git a/src/quota.rs b/src/quota.rs index 64f9c47..30f450b 100644 --- a/src/quota.rs +++ b/src/quota.rs @@ -29,7 +29,8 @@ struct RateLimitInfo { #[derive(Debug, Clone, Serialize, Deserialize)] struct UsageResponse { - plan_type: Option, + #[serde(default, deserialize_with = "deserialize_optional_plan_type")] + plan_type: Option>, rate_limit: Option, } @@ -318,7 +319,7 @@ fn apply_quota_result(store: &mut Store, account_id: &str, quota: FetchQuotaResu .find_account_mut(account_id) .expect("quota result references an existing account"); if let Some(plan) = quota.plan_type { - account.plan_type = Some(plan); + account.plan_type = plan; } account.quota = Some(quota.quota); account.updated_at = Utc::now().timestamp(); @@ -326,7 +327,7 @@ fn apply_quota_result(store: &mut Store, account_id: &str, quota: FetchQuotaResu struct FetchQuotaResult { quota: Quota, - plan_type: Option, + plan_type: Option>, } async fn fetch_quota(access_token: &str, account_id: Option<&str>) -> Result { @@ -388,6 +389,25 @@ fn parse_quota(usage: &UsageResponse) -> Quota { } } +fn deserialize_optional_plan_type<'de, D>( + deserializer: D, +) -> Result>, D::Error> +where + D: serde::Deserializer<'de>, +{ + let value = Option::::deserialize(deserializer)?; + Ok(match value { + None => None, + Some(serde_json::Value::Null) => Some(None), + Some(serde_json::Value::String(plan)) => Some(Some(plan)), + Some(value) => { + return Err(serde::de::Error::custom(format!( + "expected plan_type to be string or null, got {value}" + ))) + } + }) +} + fn remaining_percent(window: &WindowInfo) -> i32 { 100 - window.used_percent.unwrap_or(0).clamp(0, 100) } diff --git a/src/token.rs b/src/token.rs index 62e369b..5bfc29d 100644 --- a/src/token.rs +++ b/src/token.rs @@ -69,24 +69,20 @@ pub async fn refresh_account(store: &mut Store, account_id: &str) -> Result<()> account.tokens = Some(tokens); account.requires_reauth = false; account.updated_at = Utc::now().timestamp(); - if let Some(plan) = auth - .as_ref() - .and_then(|auth| auth.chatgpt_plan_type.clone()) - { - account.plan_type = Some(plan); - } - if account.account_id.is_none() { - account.account_id = auth.as_ref().and_then(|auth| auth.account_id.clone()); - } - if account.organization_id.is_none() { - account.organization_id = - auth.as_ref().and_then(|auth| auth.organization_id.clone()); + if let Some(auth) = auth { + account.plan_type = auth.chatgpt_plan_type; + account.account_id = auth.account_id; + account.organization_id = auth.organization_id; } Ok(()) } Err(error) => { if let Some(account) = store.find_account_mut(account_id) { account.requires_reauth = true; + account.plan_type = None; + account.account_id = None; + account.organization_id = None; + account.quota = None; account.updated_at = Utc::now().timestamp(); } Err(error)