1use serde::{Deserialize, Serialize};
14
15#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
17pub struct QuotaLimitResponse {
18 pub code: Option<i64>,
19 pub msg: Option<String>,
20 pub data: Option<QuotaLimitData>,
21 #[serde(default)]
22 pub success: bool,
23}
24
25#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
27pub struct QuotaLimitData {
28 #[serde(default)]
29 pub limits: Vec<QuotaLimitEntry>,
30 #[serde(default)]
32 pub level: Option<String>,
33}
34
35#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
43#[serde(rename_all = "camelCase")]
44pub struct QuotaLimitEntry {
45 #[serde(rename = "type")]
47 pub kind: Option<String>,
48 pub unit: Option<i64>,
50 pub number: Option<i64>,
52 pub usage: Option<f64>,
54 pub remaining: Option<f64>,
56 pub next_reset_time: Option<i64>,
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65
66 #[test]
71 fn decodes_fresh_account_quota_limit() {
72 let body = br#"{
73 "code": 200,
74 "msg": "success",
75 "data": {
76 "limits": [
77 {"type":"CREDIT_LIMIT","unit":3,"number":5,"usage":28000,
78 "remaining":28000,"percentage":0,"currentValue":0},
79 {"type":"CREDIT_LIMIT","unit":6,"number":1,"usage":140000,
80 "remaining":140000,"percentage":0,"currentValue":0,
81 "nextResetTime":1757000000000}
82 ],
83 "level": "max"
84 },
85 "success": true
86 }"#;
87 let envelope: QuotaLimitResponse = serde_json::from_slice(body).expect("decode");
88 assert_eq!(envelope.code, Some(200));
89 assert!(envelope.success);
90 let data = envelope.data.expect("data");
91 assert_eq!(data.level.as_deref(), Some("max"));
92 assert_eq!(data.limits.len(), 2);
93 let five_hour = &data.limits[0];
94 assert_eq!(five_hour.kind.as_deref(), Some("CREDIT_LIMIT"));
95 assert_eq!(five_hour.unit, Some(3));
96 assert_eq!(five_hour.usage, Some(28000.0));
97 assert_eq!(five_hour.remaining, Some(28000.0));
98 assert_eq!(five_hour.next_reset_time, None, "5h entry has no reset until first request");
99 let weekly = &data.limits[1];
100 assert_eq!(weekly.unit, Some(6));
101 assert_eq!(weekly.next_reset_time, Some(1_757_000_000_000));
102 }
103
104 #[test]
108 fn decodes_after_usage_quota_limit() {
109 let body = br#"{
110 "code": 200,
111 "msg": "success",
112 "data": {
113 "limits": [
114 {"type":"CREDIT_LIMIT","unit":3,"number":5,"usage":28000,
115 "remaining":27104,"percentage":3.2,"currentValue":0,
116 "nextResetTime":1757025600000},
117 {"type":"CREDIT_LIMIT","unit":6,"number":1,"usage":140000,
118 "remaining":139000,"percentage":0.71,"currentValue":0,
119 "nextResetTime":1757000000000}
120 ],
121 "level": "max"
122 },
123 "success": true
124 }"#;
125 let data =
126 serde_json::from_slice::<QuotaLimitResponse>(body).expect("decode").data.expect("data");
127 let five_hour = &data.limits[0];
128 assert_eq!(five_hour.remaining, Some(27104.0));
129 assert_eq!(five_hour.next_reset_time, Some(1_757_025_600_000));
130 }
131
132 #[test]
136 fn decodes_failure_envelopes() {
137 let wrong_key: QuotaLimitResponse = serde_json::from_slice(
138 br#"{"code":401,"msg":"token expired or incorrect","success":false}"#,
139 )
140 .expect("decode");
141 assert!(!wrong_key.success);
142 assert_eq!(wrong_key.code, Some(401));
143 assert_eq!(wrong_key.msg.as_deref(), Some("token expired or incorrect"));
144 assert!(wrong_key.data.is_none());
145
146 let wrong_path: QuotaLimitResponse =
147 serde_json::from_slice(br#"{"code":500,"msg":"404 NOT_FOUND","success":false}"#)
148 .expect("decode");
149 assert!(!wrong_path.success);
150 assert_eq!(wrong_path.code, Some(500));
151 }
152}