Skip to main content

bulk_client/msgs/
responses.rs

1use serde::Deserialize;
2use serde_json::Value;
3
4// ─────────────────────────────────────────────────────────────────────────────
5// Order responses (mirrors trade.py OrderResponse)
6// ─────────────────────────────────────────────────────────────────────────────
7
8#[derive(Debug, Clone, Deserialize)]
9#[allow(unused)]
10pub struct Response {
11    pub order_id: Option<String>,
12    pub status: String,
13    pub message: Option<String>,
14    #[serde(skip)]
15    pub raw: Value,
16}
17
18#[allow(unused)]
19impl Response {
20    /// Indicate whether response is an error
21    pub fn is_error(&self) -> bool {
22        matches!(
23            self.status.as_str(),
24            "error"
25                | "rejectedRiskLimit"
26                | "rejectedInvalid"
27                | "rejectedDuplicate"
28                | "rejectedCrossing"
29        )
30    }
31
32    /// Indicate whether response is a placement
33    pub fn is_placement(&self) -> bool {
34        matches!(self.status.as_str(), "resting" | "working" | "filled")
35    }
36
37    /// Parse the list of statuses from a post response (same logic as Python).
38    pub(crate) fn parse_responses(data: &Value) -> Vec<Self> {
39        // WS: data.data.payload.response.data.statuses
40        // HTTP: data.response.data.statuses
41        let statuses = data["data"]["payload"]["response"]["data"]["statuses"]
42            .as_array()
43            .or_else(|| data["response"]["data"]["statuses"].as_array());
44
45        let Some(arr) = statuses else {
46            return vec![];
47        };
48
49        arr.iter()
50            .map(|entry| {
51                if let Some(body) = entry.get("error") {
52                    Response {
53                        order_id: None,
54                        status: "error".into(),
55                        message: body["message"].as_str().map(Into::into),
56                        raw: body.clone(),
57                    }
58                } else {
59                    // First key is the status string
60                    let status_key = entry
61                        .as_object()
62                        .and_then(|m| m.keys().next())
63                        .unwrap_or(&String::new())
64                        .clone();
65                    let body = &entry[&status_key];
66                    if let Some(oid) = body.get("oid") {
67                        Response {
68                            order_id: body["oid"].as_str().map(Into::into),
69                            status: status_key,
70                            message: None,
71                            raw: body.clone(),
72                        }
73                    } else {
74                        Response {
75                            order_id: None,
76                            status: status_key,
77                            message: None,
78                            raw: body.clone(),
79                        }
80                    }
81                }
82            })
83            .collect()
84    }
85}