Files
kiro.rs/src/kiro/model/usage_limits.rs
2026-03-05 21:28:41 +08:00

214 lines
5.4 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! 使用额度查询数据模型
//!
//! 包含 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<f64>,
/// 订阅信息
#[serde(default)]
pub subscription_info: Option<SubscriptionInfo>,
/// 使用量明细列表
#[serde(default)]
pub usage_breakdown_list: Vec<UsageBreakdown>,
}
/// 订阅信息
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SubscriptionInfo {
/// 订阅标题 (KIRO PRO+ / KIRO FREE 等)
#[serde(default)]
pub subscription_title: Option<String>,
}
/// 使用量明细
#[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<Vec<Bonus>>,
/// 免费试用信息
#[serde(default)]
pub free_trial_info: Option<FreeTrialInfo>,
/// 下次重置日期 (Unix 时间戳)
#[serde(default)]
#[allow(dead_code)]
pub next_date_reset: Option<f64>,
/// 使用限额
#[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<String>,
}
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<f64>,
/// 免费试用状态 (ACTIVE / EXPIRED)
#[serde(default)]
pub free_trial_status: Option<String>,
/// 使用限额
#[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
}
}