kiteconnect_async_wasm/models/portfolio/
conversions.rs1use crate::models::common::{Exchange, Product, TransactionType};
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
6#[serde(rename_all = "lowercase")]
7pub enum ConversionType {
8 Position,
10 Holding,
12}
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct ConversionRequest {
17 pub exchange: Exchange,
19
20 #[serde(rename = "tradingsymbol")]
22 pub trading_symbol: String,
23
24 #[serde(rename = "transaction_type")]
26 pub transaction_type: TransactionType,
27
28 pub quantity: u32,
30
31 #[serde(rename = "from_product")]
33 pub from_product: Product,
34
35 #[serde(rename = "to_product")]
37 pub to_product: Product,
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct ConversionResponse {
43 pub status: String,
45
46 pub message: Option<String>,
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize)]
52pub struct BulkConversionRequest {
53 pub conversions: Vec<ConversionRequest>,
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize)]
59pub struct BulkConversionResponse {
60 pub status: String,
62
63 pub results: Vec<ConversionResult>,
65
66 pub message: Option<String>,
68}
69
70#[derive(Debug, Clone, Serialize, Deserialize)]
72pub struct ConversionResult {
73 #[serde(rename = "tradingsymbol")]
75 pub trading_symbol: String,
76
77 pub exchange: Exchange,
79
80 pub status: String,
82
83 pub message: Option<String>,
85
86 pub error: Option<String>,
88}
89
90impl ConversionRequest {
91 pub fn new(
93 exchange: Exchange,
94 trading_symbol: String,
95 transaction_type: TransactionType,
96 quantity: u32,
97 from_product: Product,
98 to_product: Product,
99 ) -> Self {
100 Self {
101 exchange,
102 trading_symbol,
103 transaction_type,
104 quantity,
105 from_product,
106 to_product,
107 }
108 }
109
110 pub fn cnc_to_mis(
112 exchange: Exchange,
113 trading_symbol: String,
114 transaction_type: TransactionType,
115 quantity: u32,
116 ) -> Self {
117 Self::new(
118 exchange,
119 trading_symbol,
120 transaction_type,
121 quantity,
122 Product::CNC,
123 Product::MIS,
124 )
125 }
126
127 pub fn mis_to_cnc(
129 exchange: Exchange,
130 trading_symbol: String,
131 transaction_type: TransactionType,
132 quantity: u32,
133 ) -> Self {
134 Self::new(
135 exchange,
136 trading_symbol,
137 transaction_type,
138 quantity,
139 Product::MIS,
140 Product::CNC,
141 )
142 }
143
144 pub fn nrml_to_mis(
146 exchange: Exchange,
147 trading_symbol: String,
148 transaction_type: TransactionType,
149 quantity: u32,
150 ) -> Self {
151 Self::new(
152 exchange,
153 trading_symbol,
154 transaction_type,
155 quantity,
156 Product::NRML,
157 Product::MIS,
158 )
159 }
160
161 pub fn mis_to_nrml(
163 exchange: Exchange,
164 trading_symbol: String,
165 transaction_type: TransactionType,
166 quantity: u32,
167 ) -> Self {
168 Self::new(
169 exchange,
170 trading_symbol,
171 transaction_type,
172 quantity,
173 Product::MIS,
174 Product::NRML,
175 )
176 }
177
178 pub fn conversion_type(&self) -> ConversionType {
180 match (&self.from_product, &self.to_product) {
181 (Product::CNC, Product::MIS) => ConversionType::Position,
182 (Product::MIS, Product::CNC) => ConversionType::Holding,
183 (Product::NRML, Product::MIS) => ConversionType::Position,
184 (Product::MIS, Product::NRML) => ConversionType::Position,
185 _ => ConversionType::Position, }
187 }
188
189 pub fn is_valid_conversion(&self) -> bool {
191 matches!(
193 (&self.from_product, &self.to_product),
194 (Product::CNC, Product::MIS)
195 | (Product::MIS, Product::CNC)
196 | (Product::NRML, Product::MIS)
197 | (Product::MIS, Product::NRML)
198 )
199 }
200
201 pub fn validate(&self) -> Result<(), String> {
203 if self.trading_symbol.is_empty() {
204 return Err("Trading symbol cannot be empty".to_string());
205 }
206
207 if self.quantity == 0 {
208 return Err("Quantity must be greater than 0".to_string());
209 }
210
211 if !self.is_valid_conversion() {
212 return Err(format!(
213 "Invalid conversion from {:?} to {:?}",
214 self.from_product, self.to_product
215 ));
216 }
217
218 Ok(())
219 }
220}
221
222impl BulkConversionRequest {
223 pub fn new() -> Self {
225 Self {
226 conversions: Vec::new(),
227 }
228 }
229
230 pub fn add_conversion(mut self, conversion: ConversionRequest) -> Self {
232 self.conversions.push(conversion);
233 self
234 }
235
236 pub fn add_conversions(mut self, conversions: Vec<ConversionRequest>) -> Self {
238 self.conversions.extend(conversions);
239 self
240 }
241
242 pub fn validate(&self) -> Result<(), Vec<String>> {
244 let mut errors = Vec::new();
245
246 if self.conversions.is_empty() {
247 errors.push("Bulk conversion request cannot be empty".to_string());
248 }
249
250 for (index, conversion) in self.conversions.iter().enumerate() {
251 if let Err(error) = conversion.validate() {
252 errors.push(format!("Conversion {}: {}", index + 1, error));
253 }
254 }
255
256 if errors.is_empty() {
257 Ok(())
258 } else {
259 Err(errors)
260 }
261 }
262
263 pub fn count(&self) -> usize {
265 self.conversions.len()
266 }
267
268 pub fn is_empty(&self) -> bool {
270 self.conversions.is_empty()
271 }
272}
273
274impl Default for BulkConversionRequest {
275 fn default() -> Self {
276 Self::new()
277 }
278}
279
280impl ConversionResponse {
281 pub fn is_success(&self) -> bool {
283 self.status.to_lowercase() == "success"
284 }
285
286 pub fn is_failure(&self) -> bool {
288 !self.is_success()
289 }
290}
291
292impl BulkConversionResponse {
293 pub fn is_all_success(&self) -> bool {
295 self.results.iter().all(|r| r.is_success())
296 }
297
298 pub fn success_count(&self) -> usize {
300 self.results.iter().filter(|r| r.is_success()).count()
301 }
302
303 pub fn failure_count(&self) -> usize {
305 self.results.iter().filter(|r| r.is_failure()).count()
306 }
307
308 pub fn successful_conversions(&self) -> Vec<&ConversionResult> {
310 self.results.iter().filter(|r| r.is_success()).collect()
311 }
312
313 pub fn failed_conversions(&self) -> Vec<&ConversionResult> {
315 self.results.iter().filter(|r| r.is_failure()).collect()
316 }
317}
318
319impl ConversionResult {
320 pub fn is_success(&self) -> bool {
322 self.status.to_lowercase() == "success"
323 }
324
325 pub fn is_failure(&self) -> bool {
327 !self.is_success()
328 }
329
330 pub fn error_message(&self) -> Option<&str> {
332 self.error.as_deref().or(self.message.as_deref())
333 }
334}