1use std::fmt;
5use std::str::FromStr;
6
7use serde::{Deserialize, Serialize};
8
9#[derive(Serialize)]
10pub(crate) struct EmptyRequest {}
11
12pub(crate) fn deserialize_vec_or_empty_string<'de, D, T>(
19 deserializer: D,
20) -> Result<Vec<T>, D::Error>
21where
22 D: serde::Deserializer<'de>,
23 T: Deserialize<'de>,
24{
25 #[derive(Deserialize)]
26 #[serde(untagged)]
27 enum WireValue<T> {
28 Sequence(Vec<T>),
29 String(String),
30 Null(()),
31 }
32
33 match WireValue::<T>::deserialize(deserializer)? {
34 WireValue::Sequence(values) => Ok(values),
35 WireValue::String(value) if value.is_empty() => Ok(Vec::new()),
36 WireValue::String(value) => Err(serde::de::Error::custom(format!(
37 "expected an array or empty string, got {value:?}"
38 ))),
39 WireValue::Null(()) => Ok(Vec::new()),
40 }
41}
42
43#[derive(Debug, Clone, Deserialize)]
47pub(crate) struct OkxResponse<D> {
48 pub code: String,
49 pub msg: String,
50 pub data: D,
51}
52
53#[derive(Debug, Clone, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
70#[serde(transparent)]
71pub struct NumberString(String);
72
73impl NumberString {
74 pub fn as_str(&self) -> &str {
76 &self.0
77 }
78
79 pub fn is_empty(&self) -> bool {
82 self.0.is_empty()
83 }
84
85 pub fn parse<T: FromStr>(&self) -> Result<T, T::Err> {
88 self.0.parse()
89 }
90
91 pub fn into_string(self) -> String {
93 self.0
94 }
95
96 #[cfg(feature = "rust-decimal")]
98 pub fn to_decimal(&self) -> Result<rust_decimal::Decimal, rust_decimal::Error> {
99 self.0.parse()
100 }
101}
102
103impl From<String> for NumberString {
104 fn from(s: String) -> Self {
105 NumberString(s)
106 }
107}
108
109impl From<&str> for NumberString {
110 fn from(s: &str) -> Self {
111 NumberString(s.to_owned())
112 }
113}
114
115impl AsRef<str> for NumberString {
116 fn as_ref(&self) -> &str {
117 &self.0
118 }
119}
120
121impl fmt::Display for NumberString {
122 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123 f.write_str(&self.0)
124 }
125}
126
127macro_rules! string_enum {
131 (
132 $(#[$meta:meta])*
133 $vis:vis enum $name:ident {
134 $( $(#[$vmeta:meta])* $variant:ident = $wire:literal ),* $(,)?
135 }
136 ) => {
137 $(#[$meta])*
138 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
139 #[non_exhaustive]
140 $vis enum $name {
141 $( $(#[$vmeta])* $variant, )*
142 Unknown(String),
145 }
146
147 impl $name {
148 pub fn as_str(&self) -> &str {
150 match self {
151 $( $name::$variant => $wire, )*
152 $name::Unknown(s) => s.as_str(),
153 }
154 }
155 }
156
157 impl ::core::convert::From<&str> for $name {
158 fn from(s: &str) -> Self {
159 match s {
160 $( $wire => $name::$variant, )*
161 other => $name::Unknown(other.to_owned()),
162 }
163 }
164 }
165
166 impl ::core::fmt::Display for $name {
167 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
168 f.write_str(self.as_str())
169 }
170 }
171
172 impl ::serde::Serialize for $name {
173 fn serialize<S: ::serde::Serializer>(&self, ser: S) -> ::core::result::Result<S::Ok, S::Error> {
174 ser.serialize_str(self.as_str())
175 }
176 }
177
178 impl<'de> ::serde::Deserialize<'de> for $name {
179 fn deserialize<D: ::serde::Deserializer<'de>>(de: D) -> ::core::result::Result<Self, D::Error> {
180 let s = <::std::string::String as ::serde::Deserialize>::deserialize(de)?;
181 ::core::result::Result::Ok($name::from(s.as_str()))
182 }
183 }
184 };
185}
186
187string_enum! {
188 pub enum InstType {
190 Spot = "SPOT",
192 Margin = "MARGIN",
194 Swap = "SWAP",
196 Futures = "FUTURES",
198 Option = "OPTION",
200 Events = "EVENTS",
202 }
203}
204
205string_enum! {
206 pub enum OrderSide {
208 Buy = "buy",
210 Sell = "sell",
212 }
213}
214
215string_enum! {
216 pub enum OrderType {
218 Market = "market",
220 Limit = "limit",
222 PostOnly = "post_only",
224 Fok = "fok",
226 Ioc = "ioc",
228 OptimalLimitIoc = "optimal_limit_ioc",
230 }
231}
232
233string_enum! {
234 pub enum TradeMode {
236 Cash = "cash",
238 Cross = "cross",
240 Isolated = "isolated",
242 SpotIsolated = "spot_isolated",
244 }
245}
246
247string_enum! {
248 pub enum PositionSide {
250 Long = "long",
252 Short = "short",
254 Net = "net",
256 }
257}
258
259string_enum! {
260 pub enum OrderState {
262 Live = "live",
264 PartiallyFilled = "partially_filled",
266 Filled = "filled",
268 Canceled = "canceled",
270 MmpCanceled = "mmp_canceled",
272 }
273}
274
275#[cfg(test)]
276mod tests {
277 use super::*;
278
279 #[test]
280 fn number_string_parses_and_preserves() {
281 let n = NumberString::from("1.005");
282 assert_eq!(n.as_str(), "1.005");
283 assert_eq!(n.parse::<f64>().unwrap(), 1.005);
284 assert_eq!(n.into_string(), "1.005");
285 }
286
287 #[test]
288 fn known_enum_value_round_trips() {
289 let v: InstType = serde_json::from_str("\"SWAP\"").unwrap();
290 assert_eq!(v, InstType::Swap);
291 assert_eq!(serde_json::to_string(&v).unwrap(), "\"SWAP\"");
292 }
293
294 #[test]
295 fn unknown_enum_value_is_preserved_not_an_error() {
296 let v: InstType = serde_json::from_str("\"FUTURE_THING\"").unwrap();
297 assert_eq!(v, InstType::Unknown("FUTURE_THING".to_owned()));
298 assert_eq!(serde_json::to_string(&v).unwrap(), "\"FUTURE_THING\"");
300 }
301}