1use serde::Serialize;
2
3use crate::model::{
4 OrderSide, RequestValidationError, ValidateRequest, non_empty, range_u64,
5 validate_client_request_id, validate_side,
6};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
10#[non_exhaustive]
11pub enum ConvertMode {
12 #[serde(rename = "0")]
14 Standard,
15 #[serde(rename = "1")]
17 LargeOrderVip,
18}
19
20#[derive(Debug, Clone, Copy, Default, Serialize)]
22pub struct ConvertCurrenciesRequest {}
23
24impl ConvertCurrenciesRequest {
25 pub const fn new() -> Self {
27 Self {}
28 }
29}
30
31impl ValidateRequest for ConvertCurrenciesRequest {
32 fn validate(&self) -> Result<(), RequestValidationError> {
33 Ok(())
34 }
35}
36
37#[derive(Debug, Clone, Serialize)]
39#[serde(rename_all = "camelCase")]
40pub struct ConvertCurrencyPairRequest {
41 from_ccy: String,
42 to_ccy: String,
43 #[serde(skip_serializing_if = "Option::is_none")]
44 convert_mode: Option<ConvertMode>,
45}
46
47impl ConvertCurrencyPairRequest {
48 pub fn new(from_ccy: impl Into<String>, to_ccy: impl Into<String>) -> Self {
50 Self {
51 from_ccy: from_ccy.into(),
52 to_ccy: to_ccy.into(),
53 convert_mode: None,
54 }
55 }
56
57 pub fn convert_mode(mut self, convert_mode: ConvertMode) -> Self {
59 self.convert_mode = Some(convert_mode);
60 self
61 }
62}
63
64impl ValidateRequest for ConvertCurrencyPairRequest {
65 fn validate(&self) -> Result<(), RequestValidationError> {
66 non_empty("fromCcy", &self.from_ccy)?;
67 non_empty("toCcy", &self.to_ccy)
68 }
69}
70
71#[derive(Debug, Clone, Serialize)]
73#[serde(rename_all = "camelCase")]
74pub struct ConvertQuoteRequest {
75 base_ccy: String,
76 quote_ccy: String,
77 side: OrderSide,
78 rfq_sz: String,
79 rfq_sz_ccy: String,
80 #[serde(skip_serializing_if = "Option::is_none")]
81 cl_q_req_id: Option<String>,
82 #[serde(skip_serializing_if = "Option::is_none")]
83 tag: Option<String>,
84 #[serde(skip_serializing_if = "Option::is_none")]
85 convert_mode: Option<ConvertMode>,
86}
87
88impl ConvertQuoteRequest {
89 pub fn new(
91 base_ccy: impl Into<String>,
92 quote_ccy: impl Into<String>,
93 side: OrderSide,
94 rfq_sz: impl Into<String>,
95 rfq_sz_ccy: impl Into<String>,
96 ) -> Self {
97 Self {
98 base_ccy: base_ccy.into(),
99 quote_ccy: quote_ccy.into(),
100 side,
101 rfq_sz: rfq_sz.into(),
102 rfq_sz_ccy: rfq_sz_ccy.into(),
103 cl_q_req_id: None,
104 tag: None,
105 convert_mode: None,
106 }
107 }
108
109 pub fn client_quote_request_id(mut self, id: impl Into<String>) -> Self {
111 self.cl_q_req_id = Some(id.into());
112 self
113 }
114
115 pub fn tag(mut self, tag: impl Into<String>) -> Self {
117 self.tag = Some(tag.into());
118 self
119 }
120
121 pub fn convert_mode(mut self, convert_mode: ConvertMode) -> Self {
123 self.convert_mode = Some(convert_mode);
124 self
125 }
126}
127
128impl ValidateRequest for ConvertQuoteRequest {
129 fn validate(&self) -> Result<(), RequestValidationError> {
130 non_empty("baseCcy", &self.base_ccy)?;
131 non_empty("quoteCcy", &self.quote_ccy)?;
132 validate_side(&self.side)?;
133 non_empty("rfqSz", &self.rfq_sz)?;
134 non_empty("rfqSzCcy", &self.rfq_sz_ccy)?;
135 validate_client_request_id("clQReqId", self.cl_q_req_id.as_deref())
136 }
137}
138
139#[derive(Debug, Clone, Serialize)]
141#[serde(rename_all = "camelCase")]
142pub struct ConvertTradeRequest {
143 quote_id: String,
144 base_ccy: String,
145 quote_ccy: String,
146 side: OrderSide,
147 sz: String,
148 sz_ccy: String,
149 #[serde(skip_serializing_if = "Option::is_none")]
150 cl_t_req_id: Option<String>,
151 #[serde(skip_serializing_if = "Option::is_none")]
152 tag: Option<String>,
153 #[serde(skip_serializing_if = "Option::is_none")]
154 convert_mode: Option<ConvertMode>,
155}
156
157impl ConvertTradeRequest {
158 pub fn new(
160 quote_id: impl Into<String>,
161 base_ccy: impl Into<String>,
162 quote_ccy: impl Into<String>,
163 side: OrderSide,
164 sz: impl Into<String>,
165 sz_ccy: impl Into<String>,
166 ) -> Self {
167 Self {
168 quote_id: quote_id.into(),
169 base_ccy: base_ccy.into(),
170 quote_ccy: quote_ccy.into(),
171 side,
172 sz: sz.into(),
173 sz_ccy: sz_ccy.into(),
174 cl_t_req_id: None,
175 tag: None,
176 convert_mode: None,
177 }
178 }
179
180 pub fn client_trade_request_id(mut self, id: impl Into<String>) -> Self {
182 self.cl_t_req_id = Some(id.into());
183 self
184 }
185
186 pub fn tag(mut self, tag: impl Into<String>) -> Self {
188 self.tag = Some(tag.into());
189 self
190 }
191
192 pub fn convert_mode(mut self, convert_mode: ConvertMode) -> Self {
194 self.convert_mode = Some(convert_mode);
195 self
196 }
197}
198
199impl ValidateRequest for ConvertTradeRequest {
200 fn validate(&self) -> Result<(), RequestValidationError> {
201 non_empty("quoteId", &self.quote_id)?;
202 non_empty("baseCcy", &self.base_ccy)?;
203 non_empty("quoteCcy", &self.quote_ccy)?;
204 validate_side(&self.side)?;
205 non_empty("sz", &self.sz)?;
206 non_empty("szCcy", &self.sz_ccy)?;
207 validate_client_request_id("clTReqId", self.cl_t_req_id.as_deref())
208 }
209}
210
211#[derive(Debug, Clone, Default, Serialize)]
213#[serde(rename_all = "camelCase")]
214pub struct ConvertHistoryRequest {
215 #[serde(skip_serializing_if = "Option::is_none")]
216 cl_t_req_id: Option<String>,
217 #[serde(skip_serializing_if = "Option::is_none")]
218 after: Option<String>,
219 #[serde(skip_serializing_if = "Option::is_none")]
220 before: Option<String>,
221 #[serde(skip_serializing_if = "Option::is_none")]
222 limit: Option<String>,
223 #[serde(skip_serializing_if = "Option::is_none")]
224 tag: Option<String>,
225}
226
227impl ConvertHistoryRequest {
228 pub fn new() -> Self {
230 Self::default()
231 }
232
233 pub fn client_trade_request_id(mut self, id: impl Into<String>) -> Self {
235 self.cl_t_req_id = Some(id.into());
236 self
237 }
238
239 pub fn after(mut self, timestamp_ms: u64) -> Self {
241 self.after = Some(timestamp_ms.to_string());
242 self
243 }
244
245 pub fn before(mut self, timestamp_ms: u64) -> Self {
247 self.before = Some(timestamp_ms.to_string());
248 self
249 }
250
251 pub fn limit(mut self, limit: u8) -> Self {
253 self.limit = Some(limit.to_string());
254 self
255 }
256
257 pub fn tag(mut self, tag: impl Into<String>) -> Self {
259 self.tag = Some(tag.into());
260 self
261 }
262}
263
264impl ValidateRequest for ConvertHistoryRequest {
265 fn validate(&self) -> Result<(), RequestValidationError> {
266 validate_client_request_id("clTReqId", self.cl_t_req_id.as_deref())?;
267
268 match &self.limit {
269 Some(limit) => {
270 let parsed = limit
271 .parse::<u64>()
272 .expect("ConvertHistoryRequest::limit stores a u8 as decimal text");
273
274 range_u64("limit", parsed, 1, 100)
275 }
276 None => Ok(()),
277 }
278 }
279}