//! 使用额度查询数据模型 //! //! 包含 getUsageLimits API 的响应类型定义 use serde::Deserialize; /// 使用额度查询响应 #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "camelCase")] pub struct UsageLimitsResponse { /// 下次重置日期 (Unix 时间戳) #[serde(default)] #[allow(dead_code)] pub next_date_reset: Option, /// 订阅信息 #[serde(default)] pub subscription_info: Option, /// 使用量明细列表 #[serde(default)] pub usage_breakdown_list: Vec, } /// 订阅信息 #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "camelCase")] pub struct SubscriptionInfo { /// 订阅标题 (KIRO PRO+ / KIRO FREE 等) #[serde(default)] pub subscription_title: Option, } /// 使用量明细 #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "camelCase")] pub struct UsageBreakdown { /// 当前使用量 #[serde(default)] #[allow(dead_code)] pub current_usage: i64, /// 当前使用量(精确值) #[serde(default)] pub current_usage_with_precision: f64, /// 奖励额度列表(可能为 null) #[serde(default)] pub bonuses: Option>, /// 免费试用信息 #[serde(default)] pub free_trial_info: Option, /// 下次重置日期 (Unix 时间戳) #[serde(default)] #[allow(dead_code)] pub next_date_reset: Option, /// 使用限额 #[serde(default)] #[allow(dead_code)] pub usage_limit: i64, /// 使用限额(精确值) #[serde(default)] pub usage_limit_with_precision: f64, } /// 奖励额度 #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Bonus { /// 当前使用量 #[serde(default)] pub current_usage: f64, /// 使用限额 #[serde(default)] pub usage_limit: f64, /// 状态 (ACTIVE / EXPIRED) #[serde(default)] pub status: Option, } impl Bonus { /// 检查 bonus 是否处于激活状态(大小写不敏感) pub fn is_active(&self) -> bool { self.status .as_deref() .map(|s| s.eq_ignore_ascii_case("ACTIVE")) .unwrap_or(false) } } /// 免费试用信息 #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "camelCase")] pub struct FreeTrialInfo { /// 当前使用量 #[serde(default)] #[allow(dead_code)] pub current_usage: i64, /// 当前使用量(精确值) #[serde(default)] pub current_usage_with_precision: f64, /// 免费试用过期时间 (Unix 时间戳) #[serde(default)] #[allow(dead_code)] pub free_trial_expiry: Option, /// 免费试用状态 (ACTIVE / EXPIRED) #[serde(default)] pub free_trial_status: Option, /// 使用限额 #[serde(default)] #[allow(dead_code)] pub usage_limit: i64, /// 使用限额(精确值) #[serde(default)] pub usage_limit_with_precision: f64, } // ============ 便捷方法实现 ============ impl FreeTrialInfo { /// 检查免费试用是否处于激活状态 pub fn is_active(&self) -> bool { self.free_trial_status .as_deref() .map(|s| s == "ACTIVE") .unwrap_or(false) } } impl UsageLimitsResponse { /// 获取订阅标题 pub fn subscription_title(&self) -> Option<&str> { self.subscription_info .as_ref() .and_then(|info| info.subscription_title.as_deref()) } /// 获取第一个使用量明细 fn primary_breakdown(&self) -> Option<&UsageBreakdown> { self.usage_breakdown_list.first() } /// 获取总使用限额(精确值) /// /// 累加基础额度、激活的免费试用额度和激活的奖励额度 #[allow(clippy::collapsible_if)] pub fn usage_limit(&self) -> f64 { let Some(breakdown) = self.primary_breakdown() else { return 0.0; }; let mut total = breakdown.usage_limit_with_precision; // 累加激活的 free trial 额度 if let Some(trial) = &breakdown.free_trial_info { if trial.is_active() { total += trial.usage_limit_with_precision; } } // 累加激活的 bonus 额度 if let Some(bonuses) = &breakdown.bonuses { for bonus in bonuses { if bonus.is_active() { total += bonus.usage_limit; } } } total } /// 获取总当前使用量(精确值) /// /// 累加基础使用量、激活的免费试用使用量和激活的奖励使用量 #[allow(clippy::collapsible_if)] pub fn current_usage(&self) -> f64 { let Some(breakdown) = self.primary_breakdown() else { return 0.0; }; let mut total = breakdown.current_usage_with_precision; // 累加激活的 free trial 使用量 if let Some(trial) = &breakdown.free_trial_info { if trial.is_active() { total += trial.current_usage_with_precision; } } // 累加激活的 bonus 使用量 if let Some(bonuses) = &breakdown.bonuses { for bonus in bonuses { if bonus.is_active() { total += bonus.current_usage; } } } total } }