1use serde::Serialize;
7
8use super::common::OrderType;
9
10#[derive(Debug, Clone, Serialize)]
35#[serde(rename_all = "camelCase")]
36pub struct CreateOrder {
37 pub item_id: String,
39
40 #[serde(rename = "type")]
42 pub order_type: OrderType,
43
44 pub platinum: u32,
46
47 pub quantity: u32,
49
50 pub visible: bool,
52
53 #[serde(skip_serializing_if = "Option::is_none")]
55 pub per_trade: Option<u32>,
56
57 #[serde(skip_serializing_if = "Option::is_none")]
59 pub rank: Option<u8>,
60
61 #[serde(skip_serializing_if = "Option::is_none")]
63 pub charges: Option<u8>,
64
65 #[serde(skip_serializing_if = "Option::is_none")]
67 pub subtype: Option<String>,
68
69 #[serde(skip_serializing_if = "Option::is_none")]
71 pub amber_stars: Option<u8>,
72
73 #[serde(skip_serializing_if = "Option::is_none")]
75 pub cyan_stars: Option<u8>,
76}
77
78impl CreateOrder {
79 pub fn sell(item_id: impl Into<String>, platinum: u32, quantity: u32) -> Self {
91 debug_assert!(platinum > 0, "platinum must be greater than 0");
92 debug_assert!(quantity > 0, "quantity must be greater than 0");
93 Self {
94 item_id: item_id.into(),
95 order_type: OrderType::Sell,
96 platinum,
97 quantity,
98 visible: true,
99 per_trade: None,
100 rank: None,
101 charges: None,
102 subtype: None,
103 amber_stars: None,
104 cyan_stars: None,
105 }
106 }
107
108 pub fn buy(item_id: impl Into<String>, platinum: u32, quantity: u32) -> Self {
120 debug_assert!(platinum > 0, "platinum must be greater than 0");
121 debug_assert!(quantity > 0, "quantity must be greater than 0");
122 Self {
123 item_id: item_id.into(),
124 order_type: OrderType::Buy,
125 platinum,
126 quantity,
127 visible: true,
128 per_trade: None,
129 rank: None,
130 charges: None,
131 subtype: None,
132 amber_stars: None,
133 cyan_stars: None,
134 }
135 }
136
137 pub fn with_mod_rank(mut self, rank: u8) -> Self {
139 self.rank = Some(rank);
140 self
141 }
142
143 pub fn with_charges(mut self, charges: u8) -> Self {
145 self.charges = Some(charges);
146 self
147 }
148
149 pub fn with_subtype(mut self, subtype: impl Into<String>) -> Self {
151 self.subtype = Some(subtype.into());
152 self
153 }
154
155 pub fn with_sculpture_stars(mut self, amber: u8, cyan: u8) -> Self {
157 self.amber_stars = Some(amber);
158 self.cyan_stars = Some(cyan);
159 self
160 }
161
162 pub fn with_per_trade(mut self, per_trade: u32) -> Self {
164 self.per_trade = Some(per_trade);
165 self
166 }
167
168 pub fn hidden(mut self) -> Self {
170 self.visible = false;
171 self
172 }
173
174 pub fn visible(mut self) -> Self {
176 self.visible = true;
177 self
178 }
179}
180
181#[derive(Debug, Clone, Default, Serialize)]
207#[serde(rename_all = "camelCase")]
208pub struct UpdateOrder {
209 #[serde(skip_serializing_if = "Option::is_none")]
211 pub platinum: Option<u32>,
212
213 #[serde(skip_serializing_if = "Option::is_none")]
215 pub quantity: Option<u32>,
216
217 #[serde(skip_serializing_if = "Option::is_none")]
219 pub visible: Option<bool>,
220
221 #[serde(skip_serializing_if = "Option::is_none")]
223 pub per_trade: Option<u32>,
224
225 #[serde(skip_serializing_if = "Option::is_none")]
227 pub rank: Option<u8>,
228
229 #[serde(skip_serializing_if = "Option::is_none")]
231 pub charges: Option<u8>,
232
233 #[serde(skip_serializing_if = "Option::is_none")]
235 pub amber_stars: Option<u8>,
236
237 #[serde(skip_serializing_if = "Option::is_none")]
239 pub cyan_stars: Option<u8>,
240
241 #[serde(skip_serializing_if = "Option::is_none")]
243 pub subtype: Option<String>,
244}
245
246impl UpdateOrder {
247 pub fn new() -> Self {
249 Self::default()
250 }
251
252 pub fn platinum(mut self, platinum: u32) -> Self {
258 debug_assert!(platinum > 0, "platinum must be greater than 0");
259 self.platinum = Some(platinum);
260 self
261 }
262
263 pub fn quantity(mut self, quantity: u32) -> Self {
269 debug_assert!(quantity > 0, "quantity must be greater than 0");
270 self.quantity = Some(quantity);
271 self
272 }
273
274 pub fn visible(mut self, visible: bool) -> Self {
276 self.visible = Some(visible);
277 self
278 }
279
280 pub fn per_trade(mut self, per_trade: u32) -> Self {
282 self.per_trade = Some(per_trade);
283 self
284 }
285
286 pub fn rank(mut self, rank: u8) -> Self {
288 self.rank = Some(rank);
289 self
290 }
291
292 pub fn charges(mut self, charges: u8) -> Self {
294 self.charges = Some(charges);
295 self
296 }
297
298 pub fn amber_stars(mut self, stars: u8) -> Self {
300 self.amber_stars = Some(stars);
301 self
302 }
303
304 pub fn cyan_stars(mut self, stars: u8) -> Self {
306 self.cyan_stars = Some(stars);
307 self
308 }
309
310 pub fn subtype(mut self, subtype: impl Into<String>) -> Self {
312 self.subtype = Some(subtype.into());
313 self
314 }
315
316 pub fn is_empty(&self) -> bool {
318 self.platinum.is_none()
319 && self.quantity.is_none()
320 && self.visible.is_none()
321 && self.per_trade.is_none()
322 && self.rank.is_none()
323 && self.charges.is_none()
324 && self.amber_stars.is_none()
325 && self.cyan_stars.is_none()
326 && self.subtype.is_none()
327 }
328}
329
330#[derive(Debug, Clone, Default, Serialize)]
354#[serde(rename_all = "camelCase")]
355pub struct TopOrderFilters {
356 #[serde(skip_serializing_if = "Option::is_none")]
358 pub rank: Option<u8>,
359
360 #[serde(skip_serializing_if = "Option::is_none")]
362 pub rank_lt: Option<u8>,
363
364 #[serde(skip_serializing_if = "Option::is_none")]
366 pub charges: Option<u8>,
367
368 #[serde(skip_serializing_if = "Option::is_none")]
370 pub charges_lt: Option<u8>,
371
372 #[serde(skip_serializing_if = "Option::is_none")]
374 pub amber_stars: Option<u8>,
375
376 #[serde(skip_serializing_if = "Option::is_none")]
378 pub amber_stars_lt: Option<u8>,
379
380 #[serde(skip_serializing_if = "Option::is_none")]
382 pub cyan_stars: Option<u8>,
383
384 #[serde(skip_serializing_if = "Option::is_none")]
386 pub cyan_stars_lt: Option<u8>,
387
388 #[serde(skip_serializing_if = "Option::is_none")]
390 pub subtype: Option<String>,
391}
392
393impl TopOrderFilters {
394 pub fn new() -> Self {
396 Self::default()
397 }
398
399 pub fn rank(mut self, rank: u8) -> Self {
401 self.rank = Some(rank);
402 self
403 }
404
405 pub fn rank_lt(mut self, rank: u8) -> Self {
411 debug_assert!(rank > 0, "rank_lt must be greater than 0");
412 self.rank_lt = Some(rank);
413 self
414 }
415
416 pub fn charges(mut self, charges: u8) -> Self {
418 self.charges = Some(charges);
419 self
420 }
421
422 pub fn charges_lt(mut self, charges: u8) -> Self {
428 debug_assert!(charges > 0, "charges_lt must be greater than 0");
429 self.charges_lt = Some(charges);
430 self
431 }
432
433 pub fn amber_stars(mut self, stars: u8) -> Self {
435 self.amber_stars = Some(stars);
436 self
437 }
438
439 pub fn amber_stars_lt(mut self, stars: u8) -> Self {
445 debug_assert!(stars > 0, "amber_stars_lt must be greater than 0");
446 self.amber_stars_lt = Some(stars);
447 self
448 }
449
450 pub fn cyan_stars(mut self, stars: u8) -> Self {
452 self.cyan_stars = Some(stars);
453 self
454 }
455
456 pub fn cyan_stars_lt(mut self, stars: u8) -> Self {
462 debug_assert!(stars > 0, "cyan_stars_lt must be greater than 0");
463 self.cyan_stars_lt = Some(stars);
464 self
465 }
466
467 pub fn subtype(mut self, subtype: impl Into<String>) -> Self {
469 self.subtype = Some(subtype.into());
470 self
471 }
472
473 pub fn is_empty(&self) -> bool {
475 self.rank.is_none()
476 && self.rank_lt.is_none()
477 && self.charges.is_none()
478 && self.charges_lt.is_none()
479 && self.amber_stars.is_none()
480 && self.amber_stars_lt.is_none()
481 && self.cyan_stars.is_none()
482 && self.cyan_stars_lt.is_none()
483 && self.subtype.is_none()
484 }
485
486 pub(crate) fn to_query_string(&self) -> String {
488 let mut params = Vec::new();
489
490 if let Some(v) = self.rank {
491 params.push(format!("rank={}", v));
492 }
493 if let Some(v) = self.rank_lt {
494 params.push(format!("rankLt={}", v));
495 }
496 if let Some(v) = self.charges {
497 params.push(format!("charges={}", v));
498 }
499 if let Some(v) = self.charges_lt {
500 params.push(format!("chargesLt={}", v));
501 }
502 if let Some(v) = self.amber_stars {
503 params.push(format!("amberStars={}", v));
504 }
505 if let Some(v) = self.amber_stars_lt {
506 params.push(format!("amberStarsLt={}", v));
507 }
508 if let Some(v) = self.cyan_stars {
509 params.push(format!("cyanStars={}", v));
510 }
511 if let Some(v) = self.cyan_stars_lt {
512 params.push(format!("cyanStarsLt={}", v));
513 }
514 if let Some(ref v) = self.subtype {
515 params.push(format!("subtype={}", urlencoding::encode(v)));
517 }
518
519 if params.is_empty() {
520 String::new()
521 } else {
522 format!("?{}", params.join("&"))
523 }
524 }
525}
526
527#[cfg(test)]
528mod tests {
529 use super::*;
530
531 #[test]
532 fn test_create_sell_order() {
533 let order = CreateOrder::sell("test-item", 100, 5);
534
535 assert_eq!(order.item_id, "test-item");
536 assert!(matches!(order.order_type, OrderType::Sell));
537 assert_eq!(order.platinum, 100);
538 assert_eq!(order.quantity, 5);
539 assert!(order.visible);
540 }
541
542 #[test]
543 fn test_create_buy_order() {
544 let order = CreateOrder::buy("test-item", 50, 10);
545
546 assert!(matches!(order.order_type, OrderType::Buy));
547 assert_eq!(order.platinum, 50);
548 }
549
550 #[test]
551 fn test_order_builder_chain() {
552 let order = CreateOrder::sell("mod-item", 100, 1)
553 .with_mod_rank(10)
554 .hidden();
555
556 assert_eq!(order.rank, Some(10));
557 assert!(!order.visible);
558 }
559
560 #[test]
561 fn test_update_order() {
562 let update = UpdateOrder::new().platinum(90).quantity(5);
563
564 assert_eq!(update.platinum, Some(90));
565 assert_eq!(update.quantity, Some(5));
566 assert!(!update.is_empty());
567 }
568
569 #[test]
570 fn test_update_order_empty() {
571 let update = UpdateOrder::new();
572 assert!(update.is_empty());
573 }
574
575 #[test]
576 fn test_serialization() {
577 let order = CreateOrder::sell("item", 100, 1).with_mod_rank(5);
578
579 let json = serde_json::to_string(&order).unwrap();
580 assert!(json.contains("\"rank\":5"));
581 assert!(!json.contains("charges")); }
583}