1use crate::model::order::OrderInfo;
7use crate::model::order_management::QuoteResult;
8use crate::model::trade::{LastTrade, TradeExecution};
9use pretty_simple_display::{DebugPretty, DisplaySimple};
10
11use serde::{Deserialize, Serialize};
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct JsonRpcResponse<T> {
16 pub jsonrpc: String,
18 pub id: Option<serde_json::Value>,
20 pub result: Option<T>,
22 pub error: Option<JsonRpcError>,
24 pub testnet: Option<bool>,
26 #[serde(rename = "usIn")]
28 pub us_in: Option<i64>,
29 #[serde(rename = "usOut")]
31 pub us_out: Option<i64>,
32 #[serde(rename = "usDiff")]
34 pub us_diff: Option<i64>,
35}
36
37impl<T> JsonRpcResponse<T> {
38 pub fn success(id: Option<serde_json::Value>, result: T) -> Self {
40 Self {
41 jsonrpc: "2.0".to_string(),
42 id,
43 result: Some(result),
44 error: None,
45 testnet: None,
46 us_in: None,
47 us_out: None,
48 us_diff: None,
49 }
50 }
51
52 pub fn error(id: Option<serde_json::Value>, error: JsonRpcError) -> Self {
54 Self {
55 jsonrpc: "2.0".to_string(),
56 id,
57 result: None,
58 error: Some(error),
59 testnet: None,
60 us_in: None,
61 us_out: None,
62 us_diff: None,
63 }
64 }
65
66 pub fn is_success(&self) -> bool {
68 self.error.is_none() && self.result.is_some()
69 }
70
71 pub fn is_error(&self) -> bool {
73 self.error.is_some()
74 }
75
76 pub fn into_result(self) -> Result<T, JsonRpcError> {
78 match (self.result, self.error) {
79 (Some(result), None) => Ok(result),
80 (None, Some(error)) => Err(error),
81 (Some(_), Some(error)) => Err(error), (None, None) => Err(JsonRpcError {
83 code: -32603,
84 message: "Internal error: neither result nor error present".to_string(),
85 data: None,
86 }),
87 }
88 }
89}
90
91#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize, PartialEq)]
93pub struct JsonRpcError {
94 pub code: i32,
96 pub message: String,
98 pub data: Option<serde_json::Value>,
100}
101
102impl JsonRpcError {
103 pub fn new(code: i32, message: String) -> Self {
105 Self {
106 code,
107 message,
108 data: None,
109 }
110 }
111
112 pub fn with_data(code: i32, message: String, data: serde_json::Value) -> Self {
114 Self {
115 code,
116 message,
117 data: Some(data),
118 }
119 }
120
121 pub fn parse_error() -> Self {
123 Self::new(-32700, "Parse error".to_string())
124 }
125
126 pub fn invalid_request() -> Self {
128 Self::new(-32600, "Invalid Request".to_string())
129 }
130
131 pub fn method_not_found() -> Self {
133 Self::new(-32601, "Method not found".to_string())
134 }
135
136 pub fn invalid_params() -> Self {
138 Self::new(-32602, "Invalid params".to_string())
139 }
140
141 pub fn internal_error() -> Self {
143 Self::new(-32603, "Internal error".to_string())
144 }
145
146 pub fn is_server_error(&self) -> bool {
148 self.code <= -32000 && self.code >= -32099
149 }
150
151 pub fn is_application_error(&self) -> bool {
153 self.code > -32000
154 }
155}
156
157#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize)]
159pub struct AuthResponse {
160 pub access_token: String,
162 pub token_type: String,
164 pub expires_in: i64,
166 pub refresh_token: String,
168 pub scope: String,
170}
171
172#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize, PartialEq)]
174pub struct Pagination {
175 pub page: Option<u32>,
177 pub per_page: Option<u32>,
179 pub total: Option<u64>,
181 pub pages: Option<u32>,
183 pub has_more: Option<bool>,
185}
186
187#[derive(Debug, Clone, Serialize, Deserialize)]
189pub struct PaginatedResponse<T> {
190 pub data: Vec<T>,
192 pub pagination: Option<Pagination>,
194}
195
196impl<T> PaginatedResponse<T> {
197 pub fn new(data: Vec<T>) -> Self {
199 Self {
200 data,
201 pagination: None,
202 }
203 }
204
205 pub fn with_pagination(data: Vec<T>, pagination: Pagination) -> Self {
207 Self {
208 data,
209 pagination: Some(pagination),
210 }
211 }
212
213 pub fn has_more(&self) -> bool {
215 self.pagination
216 .as_ref()
217 .and_then(|p| p.has_more)
218 .unwrap_or(false)
219 }
220
221 pub fn len(&self) -> usize {
223 self.data.len()
224 }
225
226 pub fn is_empty(&self) -> bool {
228 self.data.is_empty()
229 }
230}
231
232#[derive(Debug, Clone, Serialize, Deserialize)]
234pub struct Notification<T> {
235 pub jsonrpc: String,
237 pub method: String,
239 pub params: T,
241}
242
243impl<T> Notification<T> {
244 pub fn new(method: String, params: T) -> Self {
246 Self {
247 jsonrpc: "2.0".to_string(),
248 method,
249 params,
250 }
251 }
252}
253
254#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize)]
256pub struct SubscriptionResponse {
257 pub subscription: String,
259 pub channel: String,
261}
262
263#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize)]
265pub struct HeartbeatResponse {
266 #[serde(rename = "type")]
268 pub type_: String,
269}
270
271#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize)]
273pub struct TestResponse {
274 pub version: String,
276}
277
278#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize)]
280pub struct ServerTimeResponse {
281 pub timestamp: i64,
283}
284
285#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize)]
287pub struct SettlementsResponse {
288 pub continuation: Option<String>,
290 pub settlements: Vec<crate::model::settlement::Settlement>,
292}
293
294impl SettlementsResponse {
295 pub fn new(settlements: Vec<crate::model::settlement::Settlement>) -> Self {
297 Self {
298 continuation: None,
299 settlements,
300 }
301 }
302
303 pub fn with_continuation(
305 settlements: Vec<crate::model::settlement::Settlement>,
306 continuation: String,
307 ) -> Self {
308 Self {
309 continuation: Some(continuation),
310 settlements,
311 }
312 }
313
314 pub fn has_more(&self) -> bool {
316 self.continuation.is_some()
317 }
318}
319
320#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize)]
322pub struct ContractSizeResponse {
323 pub contract_size: f64,
325}
326
327#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize)]
329pub struct StatusResponse {
330 pub locked: Option<bool>,
332 pub message: Option<String>,
334 pub locked_indices: Option<Vec<String>>,
336 #[serde(flatten)]
338 pub additional_fields: std::collections::HashMap<String, serde_json::Value>,
339}
340
341#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize)]
343pub struct AprHistoryResponse {
344 pub data: Vec<AprDataPoint>,
346 pub continuation: Option<String>,
348}
349#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize)]
351pub struct AprDataPoint {
352 pub apr: f64,
354 pub timestamp: Option<u64>,
356 pub day: i32,
358}
359
360#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize)]
362pub struct HelloResponse {
363 pub version: String,
365}
366
367#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize)]
369pub struct DeliveryPricesResponse {
370 pub data: Vec<DeliveryPriceData>,
372 pub records_total: u32,
374}
375
376#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize)]
378pub struct DeliveryPriceData {
379 pub date: String,
381 pub delivery_price: f64,
383}
384
385#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize)]
387pub struct CurrencyExpirations {
388 pub future: Option<Vec<String>>,
390 pub option: Option<Vec<String>>,
392}
393
394#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize)]
396pub struct ExpirationsResponse {
397 pub future: Option<Vec<String>>,
399 pub option: Option<Vec<String>>,
401 #[serde(flatten)]
403 pub currencies: std::collections::HashMap<String, CurrencyExpirations>,
404}
405
406#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize)]
408pub struct LastTradesResponse {
409 pub has_more: bool,
411 pub trades: Vec<LastTrade>,
413}
414
415#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize)]
417pub struct OrderResponse {
418 pub order: OrderInfo,
420 pub trades: Vec<TradeExecution>,
422}
423
424#[derive(DebugPretty, DisplaySimple, Clone, Serialize, Deserialize)]
426pub struct MassQuoteResponse {
427 pub quotes: Vec<QuoteResult>,
429}
430
431impl std::error::Error for JsonRpcError {}
432
433#[cfg(test)]
434mod tests {
435 use super::*;
436 use serde_json::json;
437
438 #[test]
439 fn test_json_rpc_response_success() {
440 let response = JsonRpcResponse::success(Some(json!(1)), "test_result".to_string());
441 assert_eq!(response.jsonrpc, "2.0");
442 assert_eq!(response.id, Some(json!(1)));
443 assert_eq!(response.result, Some("test_result".to_string()));
444 assert_eq!(response.error, None);
445 assert!(response.is_success());
446 assert!(!response.is_error());
447 }
448
449 #[test]
450 fn test_json_rpc_response_error() {
451 let error = JsonRpcError::new(-32600, "Invalid Request".to_string());
452 let response: JsonRpcResponse<String> =
453 JsonRpcResponse::error(Some(json!(1)), error.clone());
454
455 assert_eq!(response.jsonrpc, "2.0");
456 assert_eq!(response.id, Some(json!(1)));
457 assert_eq!(response.result, None);
458 assert!(response.error.is_some());
459 assert!(!response.is_success());
460 assert!(response.is_error());
461 }
462
463 #[test]
464 fn test_json_rpc_response_into_result_success() {
465 let response = JsonRpcResponse::success(Some(json!(1)), "test_result".to_string());
466 let result = response.into_result().unwrap();
467 assert_eq!(result, "test_result");
468 }
469
470 #[test]
471 fn test_json_rpc_response_into_result_error() {
472 let error = JsonRpcError::new(-32600, "Invalid Request".to_string());
473 let response: JsonRpcResponse<String> =
474 JsonRpcResponse::error(Some(json!(1)), error.clone());
475 let result = response.into_result();
476 assert!(result.is_err());
477 let err = result.unwrap_err();
478 assert_eq!(err.code, -32600);
479 assert_eq!(err.message, "Invalid Request");
480 }
481
482 #[test]
483 fn test_json_rpc_response_into_result_neither() {
484 let response: JsonRpcResponse<String> = JsonRpcResponse {
485 jsonrpc: "2.0".to_string(),
486 id: Some(json!(1)),
487 result: None,
488 error: None,
489 testnet: None,
490 us_in: None,
491 us_out: None,
492 us_diff: None,
493 };
494 let result = response.into_result();
495 assert!(result.is_err());
496 let err = result.unwrap_err();
497 assert_eq!(err.code, -32603);
498 assert!(err.message.contains("Internal error"));
499 }
500
501 #[test]
502 fn test_json_rpc_error_new() {
503 let error = JsonRpcError::new(-32600, "Invalid Request".to_string());
504 assert_eq!(error.code, -32600);
505 assert_eq!(error.message, "Invalid Request");
506 assert_eq!(error.data, None);
507 }
508
509 #[test]
510 fn test_json_rpc_error_with_data() {
511 let data = json!({"details": "Additional error information"});
512 let error = JsonRpcError::with_data(-32602, "Invalid params".to_string(), data.clone());
513 assert_eq!(error.code, -32602);
514 assert_eq!(error.message, "Invalid params");
515 assert_eq!(error.data, Some(data));
516 }
517
518 #[test]
519 fn test_json_rpc_error_standard_errors() {
520 let parse_error = JsonRpcError::parse_error();
521 assert_eq!(parse_error.code, -32700);
522 assert_eq!(parse_error.message, "Parse error");
523
524 let invalid_request = JsonRpcError::invalid_request();
525 assert_eq!(invalid_request.code, -32600);
526 assert_eq!(invalid_request.message, "Invalid Request");
527
528 let method_not_found = JsonRpcError::method_not_found();
529 assert_eq!(method_not_found.code, -32601);
530 assert_eq!(method_not_found.message, "Method not found");
531
532 let invalid_params = JsonRpcError::invalid_params();
533 assert_eq!(invalid_params.code, -32602);
534 assert_eq!(invalid_params.message, "Invalid params");
535
536 let internal_error = JsonRpcError::internal_error();
537 assert_eq!(internal_error.code, -32603);
538 assert_eq!(internal_error.message, "Internal error");
539 }
540
541 #[test]
542 fn test_json_rpc_error_is_server_error() {
543 let server_error = JsonRpcError::new(-32001, "Server error".to_string());
544 assert!(server_error.is_server_error());
545 assert!(!server_error.is_application_error());
546
547 let app_error = JsonRpcError::new(-31999, "Application error".to_string());
548 assert!(!app_error.is_server_error());
549 assert!(app_error.is_application_error());
550 }
551
552 #[test]
553 fn test_json_rpc_error_display() {
554 let error = JsonRpcError::new(-32600, "Invalid Request".to_string());
555 let display_str = format!("{}", error);
556 assert!(display_str.contains("-32600"));
558 assert!(display_str.contains("Invalid Request"));
559 }
560
561 #[test]
562 fn test_auth_response() {
563 let auth_response = AuthResponse {
564 access_token: "access_token_123".to_string(),
565 token_type: "bearer".to_string(),
566 expires_in: 3600,
567 refresh_token: "refresh_token_456".to_string(),
568 scope: "read write".to_string(),
569 };
570
571 assert_eq!(auth_response.access_token, "access_token_123");
572 assert_eq!(auth_response.token_type, "bearer");
573 assert_eq!(auth_response.expires_in, 3600);
574 assert_eq!(auth_response.refresh_token, "refresh_token_456");
575 assert_eq!(auth_response.scope, "read write");
576 }
577
578 #[test]
579 fn test_pagination() {
580 let pagination = Pagination {
581 page: Some(1),
582 per_page: Some(50),
583 total: Some(1000),
584 pages: Some(20),
585 has_more: Some(true),
586 };
587
588 assert_eq!(pagination.page, Some(1));
589 assert_eq!(pagination.per_page, Some(50));
590 assert_eq!(pagination.total, Some(1000));
591 assert_eq!(pagination.pages, Some(20));
592 assert_eq!(pagination.has_more, Some(true));
593 }
594
595 #[test]
596 fn test_paginated_response_new() {
597 let data = vec!["item1".to_string(), "item2".to_string()];
598 let response = PaginatedResponse::new(data.clone());
599
600 assert_eq!(response.data, data);
601 assert_eq!(response.pagination, None);
602 assert_eq!(response.len(), 2);
603 assert!(!response.is_empty());
604 assert!(!response.has_more());
605 }
606
607 #[test]
608 fn test_paginated_response_with_pagination() {
609 let data = vec!["item1".to_string(), "item2".to_string()];
610 let pagination = Pagination {
611 page: Some(1),
612 per_page: Some(2),
613 total: Some(10),
614 pages: Some(5),
615 has_more: Some(true),
616 };
617 let response = PaginatedResponse::with_pagination(data.clone(), pagination);
618
619 assert_eq!(response.data, data);
620 assert!(response.pagination.is_some());
621 assert_eq!(response.len(), 2);
622 assert!(!response.is_empty());
623 assert!(response.has_more());
624 }
625
626 #[test]
627 fn test_paginated_response_empty() {
628 let response: PaginatedResponse<String> = PaginatedResponse::new(vec![]);
629 assert_eq!(response.len(), 0);
630 assert!(response.is_empty());
631 assert!(!response.has_more());
632 }
633
634 #[test]
635 fn test_notification() {
636 let notification = Notification::new("ticker".to_string(), "BTC-PERPETUAL".to_string());
637 assert_eq!(notification.jsonrpc, "2.0");
638 assert_eq!(notification.method, "ticker");
639 assert_eq!(notification.params, "BTC-PERPETUAL");
640 }
641
642 #[test]
643 fn test_subscription_response() {
644 let subscription = SubscriptionResponse {
645 subscription: "sub_123".to_string(),
646 channel: "ticker.BTC-PERPETUAL".to_string(),
647 };
648 assert_eq!(subscription.subscription, "sub_123");
649 assert_eq!(subscription.channel, "ticker.BTC-PERPETUAL");
650 }
651
652 #[test]
653 fn test_heartbeat_response() {
654 let heartbeat = HeartbeatResponse {
655 type_: "heartbeat".to_string(),
656 };
657 assert_eq!(heartbeat.type_, "heartbeat");
658 }
659
660 #[test]
661 fn test_test_response() {
662 let test_response = TestResponse {
663 version: "1.2.3".to_string(),
664 };
665 assert_eq!(test_response.version, "1.2.3");
666 }
667
668 #[test]
669 fn test_server_time_response() {
670 let server_time = ServerTimeResponse {
671 timestamp: 1640995200000,
672 };
673 assert_eq!(server_time.timestamp, 1640995200000);
674 }
675
676 #[test]
677 fn test_settlements_response_new() {
678 let settlements = vec![];
679 let response = SettlementsResponse::new(settlements);
680 assert_eq!(response.continuation, None);
681 assert!(response.settlements.is_empty());
682 assert!(!response.has_more());
683 }
684
685 #[test]
686 fn test_settlements_response_with_continuation() {
687 let settlements = vec![];
688 let response = SettlementsResponse::with_continuation(settlements, "token_123".to_string());
689 assert_eq!(response.continuation, Some("token_123".to_string()));
690 assert!(response.has_more());
691 }
692
693 #[test]
694 fn test_contract_size_response() {
695 let contract_size = ContractSizeResponse { contract_size: 1.0 };
696 assert_eq!(contract_size.contract_size, 1.0);
697 }
698
699 #[test]
700 fn test_status_response() {
701 let mut additional_fields = std::collections::HashMap::new();
702 additional_fields.insert("custom_field".to_string(), json!("custom_value"));
703
704 let status = StatusResponse {
705 locked: Some(false),
706 message: Some("System operational".to_string()),
707 locked_indices: Some(vec!["BTC".to_string(), "ETH".to_string()]),
708 additional_fields,
709 };
710
711 assert_eq!(status.locked, Some(false));
712 assert_eq!(status.message, Some("System operational".to_string()));
713 assert_eq!(
714 status.locked_indices,
715 Some(vec!["BTC".to_string(), "ETH".to_string()])
716 );
717 assert_eq!(
718 status.additional_fields.get("custom_field"),
719 Some(&json!("custom_value"))
720 );
721 }
722
723 #[test]
724 fn test_apr_data_point() {
725 let apr_point = AprDataPoint {
726 apr: 5.25,
727 timestamp: Some(1640995200000),
728 day: 365,
729 };
730 assert_eq!(apr_point.apr, 5.25);
731 assert_eq!(apr_point.timestamp, Some(1640995200000));
732 assert_eq!(apr_point.day, 365);
733 }
734
735 #[test]
736 fn test_apr_history_response() {
737 let data_points = vec![AprDataPoint {
738 apr: 5.25,
739 timestamp: Some(1640995200000),
740 day: 365,
741 }];
742 let apr_history = AprHistoryResponse {
743 data: data_points,
744 continuation: Some("token_456".to_string()),
745 };
746 assert_eq!(apr_history.data.len(), 1);
747 assert_eq!(apr_history.continuation, Some("token_456".to_string()));
748 }
749
750 #[test]
751 fn test_hello_response() {
752 let hello = HelloResponse {
753 version: "2.1.1".to_string(),
754 };
755 assert_eq!(hello.version, "2.1.1");
756 }
757
758 #[test]
759 fn test_delivery_price_data() {
760 let delivery_price = DeliveryPriceData {
761 date: "2024-01-01".to_string(),
762 delivery_price: 50000.0,
763 };
764 assert_eq!(delivery_price.date, "2024-01-01");
765 assert_eq!(delivery_price.delivery_price, 50000.0);
766 }
767
768 #[test]
769 fn test_delivery_prices_response() {
770 let data = vec![DeliveryPriceData {
771 date: "2024-01-01".to_string(),
772 delivery_price: 50000.0,
773 }];
774 let delivery_prices = DeliveryPricesResponse {
775 data,
776 records_total: 1,
777 };
778 assert_eq!(delivery_prices.data.len(), 1);
779 assert_eq!(delivery_prices.records_total, 1);
780 }
781
782 #[test]
783 fn test_currency_expirations() {
784 let expirations = CurrencyExpirations {
785 future: Some(vec!["2024-03-29".to_string()]),
786 option: Some(vec!["2024-01-26".to_string(), "2024-02-23".to_string()]),
787 };
788 assert_eq!(expirations.future, Some(vec!["2024-03-29".to_string()]));
789 assert_eq!(
790 expirations.option,
791 Some(vec!["2024-01-26".to_string(), "2024-02-23".to_string()])
792 );
793 }
794
795 #[test]
796 fn test_expirations_response() {
797 let mut currencies = std::collections::HashMap::new();
798 currencies.insert(
799 "BTC".to_string(),
800 CurrencyExpirations {
801 future: Some(vec!["2024-03-29".to_string()]),
802 option: Some(vec!["2024-01-26".to_string()]),
803 },
804 );
805
806 let expirations = ExpirationsResponse {
807 future: Some(vec!["2024-03-29".to_string()]),
808 option: Some(vec!["2024-01-26".to_string()]),
809 currencies,
810 };
811
812 assert_eq!(expirations.future, Some(vec!["2024-03-29".to_string()]));
813 assert_eq!(expirations.option, Some(vec!["2024-01-26".to_string()]));
814 assert!(expirations.currencies.contains_key("BTC"));
815 }
816
817 #[test]
818 fn test_last_trades_response() {
819 let last_trades = LastTradesResponse {
820 has_more: true,
821 trades: vec![],
822 };
823 assert!(last_trades.has_more);
824 assert!(last_trades.trades.is_empty());
825 }
826
827 #[test]
828 fn test_serialization_roundtrip() {
829 let response = JsonRpcResponse::success(Some(json!(1)), "test_result".to_string());
830 let json = serde_json::to_string(&response).unwrap();
831 let deserialized: JsonRpcResponse<String> = serde_json::from_str(&json).unwrap();
832
833 assert_eq!(response.jsonrpc, deserialized.jsonrpc);
834 assert_eq!(response.id, deserialized.id);
835 assert_eq!(response.result, deserialized.result);
836 assert_eq!(response.error.is_none(), deserialized.error.is_none());
837 }
838
839 #[test]
840 fn test_debug_and_display_implementations() {
841 let auth_response = AuthResponse {
842 access_token: "token".to_string(),
843 token_type: "bearer".to_string(),
844 expires_in: 3600,
845 refresh_token: "refresh".to_string(),
846 scope: "read".to_string(),
847 };
848
849 let debug_str = format!("{:?}", auth_response);
850 let display_str = format!("{}", auth_response);
851
852 assert!(debug_str.contains("token"));
853 assert!(display_str.contains("token"));
854 }
855}