kiteconnect_async_wasm/models/auth/
user.rs1use chrono::{DateTime, Utc};
8use serde::{Deserialize, Serialize};
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct UserProfile {
13 pub user_id: String,
15
16 pub user_name: String,
18
19 pub user_shortname: String,
21
22 pub user_type: String,
24
25 pub email: String,
27
28 #[serde(default)]
30 pub phone: String,
31
32 #[serde(default)]
34 pub avatar_url: Option<String>,
35
36 pub broker: String,
38
39 pub exchanges: Vec<String>,
41
42 pub products: Vec<String>,
44
45 pub order_types: Vec<String>,
47
48 #[serde(default)]
50 pub pan: Option<String>,
51
52 #[serde(default)]
54 pub meta: Option<UserMeta>,
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize)]
59pub struct UserMeta {
60 #[serde(default)]
62 pub demat_consent: String,
63}
64
65impl UserProfile {
66 pub fn has_exchange(&self, exchange: &str) -> bool {
68 self.exchanges.iter().any(|e| e == exchange)
69 }
70
71 pub fn has_product(&self, product: &str) -> bool {
73 self.products.iter().any(|p| p == product)
74 }
75
76 pub fn has_order_type(&self, order_type: &str) -> bool {
78 self.order_types.iter().any(|o| o == order_type)
79 }
80
81 pub fn display_name(&self) -> &str {
83 if !self.user_name.is_empty() {
84 &self.user_name
85 } else {
86 &self.user_shortname
87 }
88 }
89
90 pub fn is_complete(&self) -> bool {
92 !self.user_id.is_empty()
93 && !self.email.is_empty()
94 && !self.exchanges.is_empty()
95 }
96}
97
98#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
100#[serde(rename_all = "lowercase")]
101pub enum UserType {
102 Individual,
103 Corporate,
104 Partnership,
105 Huf, }
107
108impl UserType {
109 pub fn supports_family_account(&self) -> bool {
111 matches!(self, UserType::Huf)
112 }
113
114 pub fn is_individual(&self) -> bool {
116 matches!(self, UserType::Individual)
117 }
118
119 pub fn is_business(&self) -> bool {
121 matches!(self, UserType::Corporate | UserType::Partnership)
122 }
123}
124
125impl std::fmt::Display for UserType {
126 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
127 match self {
128 UserType::Individual => write!(f, "individual"),
129 UserType::Corporate => write!(f, "corporate"),
130 UserType::Partnership => write!(f, "partnership"),
131 UserType::Huf => write!(f, "huf"),
132 }
133 }
134}
135
136impl From<String> for UserType {
137 fn from(s: String) -> Self {
138 match s.to_lowercase().as_str() {
139 "individual" => UserType::Individual,
140 "corporate" => UserType::Corporate,
141 "partnership" => UserType::Partnership,
142 "huf" => UserType::Huf,
143 _ => UserType::Individual, }
145 }
146}
147
148impl From<&str> for UserType {
149 fn from(s: &str) -> Self {
150 Self::from(s.to_string())
151 }
152}
153
154#[derive(Debug, Clone, Serialize, Deserialize)]
156pub struct AccountStatus {
157 pub active: bool,
159
160 pub trading_enabled: bool,
162
163 #[serde(default)]
165 pub created_at: Option<DateTime<Utc>>,
166
167 #[serde(default)]
169 pub last_login: Option<DateTime<Utc>>,
170
171 #[serde(default)]
173 pub restrictions: Vec<String>,
174
175 #[serde(default)]
177 pub kyc_status: Option<String>,
178}
179
180impl AccountStatus {
181 pub fn can_trade(&self) -> bool {
183 self.active && self.trading_enabled && self.restrictions.is_empty()
184 }
185
186 pub fn has_restrictions(&self) -> bool {
188 !self.restrictions.is_empty()
189 }
190
191 pub fn is_kyc_complete(&self) -> bool {
193 self.kyc_status
194 .as_ref()
195 .map(|status| status.to_lowercase() == "complete")
196 .unwrap_or(false)
197 }
198}
199
200#[cfg(test)]
201mod tests {
202 use super::*;
203
204 #[test]
205 fn test_user_profile() {
206 let profile = UserProfile {
207 user_id: "TEST123".to_string(),
208 user_name: "Test User".to_string(),
209 user_shortname: "testuser".to_string(),
210 user_type: "individual".to_string(),
211 email: "test@example.com".to_string(),
212 phone: "+91-9876543210".to_string(),
213 avatar_url: None,
214 broker: "ZERODHA".to_string(),
215 exchanges: vec!["NSE".to_string(), "BSE".to_string()],
216 products: vec!["CNC".to_string(), "MIS".to_string()],
217 order_types: vec!["MARKET".to_string(), "LIMIT".to_string()],
218 pan: Some("ABCDE1234F".to_string()),
219 meta: None,
220 };
221
222 assert!(profile.is_complete());
223 assert!(profile.has_exchange("NSE"));
224 assert!(!profile.has_exchange("MCX"));
225 assert_eq!(profile.display_name(), "Test User");
226 }
227
228 #[test]
229 fn test_user_type() {
230 let individual = UserType::Individual;
231 assert!(individual.is_individual());
232 assert!(!individual.is_business());
233 assert!(!individual.supports_family_account());
234
235 let corporate = UserType::Corporate;
236 assert!(!corporate.is_individual());
237 assert!(corporate.is_business());
238
239 let huf = UserType::Huf;
240 assert!(huf.supports_family_account());
241
242 assert_eq!(UserType::from("individual"), UserType::Individual);
244 assert_eq!(UserType::from("corporate"), UserType::Corporate);
245 assert_eq!(UserType::from("unknown"), UserType::Individual); }
247
248 #[test]
249 fn test_account_status() {
250 let mut status = AccountStatus {
251 active: true,
252 trading_enabled: true,
253 created_at: None,
254 last_login: None,
255 restrictions: vec![],
256 kyc_status: Some("complete".to_string()),
257 };
258
259 assert!(status.can_trade());
260 assert!(!status.has_restrictions());
261 assert!(status.is_kyc_complete());
262
263 status.restrictions.push("day_trading_disabled".to_string());
265 assert!(!status.can_trade());
266 assert!(status.has_restrictions());
267
268 status.kyc_status = Some("pending".to_string());
270 assert!(!status.is_kyc_complete());
271 }
272}