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)]
49pub(crate) struct OkxResponse<D> {
50 pub code: String,
51 pub msg: String,
52 pub data: D,
53}
54
55#[derive(Debug, Clone, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
72#[serde(transparent)]
73pub struct NumberString(String);
74
75impl NumberString {
76 pub fn as_str(&self) -> &str {
78 &self.0
79 }
80
81 pub fn is_empty(&self) -> bool {
84 self.0.is_empty()
85 }
86
87 pub fn parse<T: FromStr>(&self) -> Result<T, T::Err> {
90 self.0.parse()
91 }
92
93 pub fn into_string(self) -> String {
95 self.0
96 }
97
98 #[cfg(feature = "rust-decimal")]
100 pub fn to_decimal(&self) -> Result<rust_decimal::Decimal, rust_decimal::Error> {
101 self.0.parse()
102 }
103}
104
105impl From<String> for NumberString {
106 fn from(s: String) -> Self {
107 NumberString(s)
108 }
109}
110
111impl From<&str> for NumberString {
112 fn from(s: &str) -> Self {
113 NumberString(s.to_owned())
114 }
115}
116
117impl AsRef<str> for NumberString {
118 fn as_ref(&self) -> &str {
119 &self.0
120 }
121}
122
123impl fmt::Display for NumberString {
124 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125 f.write_str(&self.0)
126 }
127}
128
129macro_rules! string_enum {
133 (
134 $(#[$meta:meta])*
135 $vis:vis enum $name:ident {
136 $( $(#[$vmeta:meta])* $variant:ident = $wire:literal ),* $(,)?
137 }
138 ) => {
139 $(#[$meta])*
140 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
141 #[non_exhaustive]
142 $vis enum $name {
143 $( $(#[$vmeta])* $variant, )*
144 Unknown(String),
147 }
148
149 impl $name {
150 pub fn as_str(&self) -> &str {
152 match self {
153 $( $name::$variant => $wire, )*
154 $name::Unknown(s) => s.as_str(),
155 }
156 }
157 }
158
159 impl ::core::convert::From<&str> for $name {
160 fn from(s: &str) -> Self {
161 match s {
162 $( $wire => $name::$variant, )*
163 other => $name::Unknown(other.to_owned()),
164 }
165 }
166 }
167
168 impl ::core::fmt::Display for $name {
169 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
170 f.write_str(self.as_str())
171 }
172 }
173
174 impl ::serde::Serialize for $name {
175 fn serialize<S: ::serde::Serializer>(&self, ser: S) -> ::core::result::Result<S::Ok, S::Error> {
176 ser.serialize_str(self.as_str())
177 }
178 }
179
180 impl<'de> ::serde::Deserialize<'de> for $name {
181 fn deserialize<D: ::serde::Deserializer<'de>>(de: D) -> ::core::result::Result<Self, D::Error> {
182 let s = <::std::string::String as ::serde::Deserialize>::deserialize(de)?;
183 ::core::result::Result::Ok($name::from(s.as_str()))
184 }
185 }
186 };
187}
188
189string_enum! {
190 pub enum InstType {
192 Spot = "SPOT",
194 Margin = "MARGIN",
196 Swap = "SWAP",
198 Futures = "FUTURES",
200 Option = "OPTION",
202 Events = "EVENTS",
204 }
205}
206
207string_enum! {
208 pub enum OrderSide {
210 Buy = "buy",
212 Sell = "sell",
214 }
215}
216
217string_enum! {
218 pub enum OrderType {
220 Market = "market",
222 Limit = "limit",
224 PostOnly = "post_only",
226 Fok = "fok",
228 Ioc = "ioc",
230 OptimalLimitIoc = "optimal_limit_ioc",
232 }
233}
234
235string_enum! {
236 pub enum TradeMode {
238 Cash = "cash",
240 Cross = "cross",
242 Isolated = "isolated",
244 SpotIsolated = "spot_isolated",
246 }
247}
248
249string_enum! {
250 pub enum PositionSide {
252 Long = "long",
254 Short = "short",
256 Net = "net",
258 }
259}
260
261string_enum! {
262 pub enum OrderState {
264 Live = "live",
266 PartiallyFilled = "partially_filled",
268 Filled = "filled",
270 Canceled = "canceled",
272 MmpCanceled = "mmp_canceled",
274 }
275}
276
277#[cfg(test)]
278mod tests {
279 use super::*;
280
281 #[test]
282 fn number_string_parses_and_preserves() {
283 let n = NumberString::from("1.005");
284 assert_eq!(n.as_str(), "1.005");
285 assert_eq!(n.parse::<f64>().unwrap(), 1.005);
286 assert_eq!(n.into_string(), "1.005");
287 }
288
289 #[test]
290 fn known_enum_value_round_trips() {
291 let v: InstType = serde_json::from_str("\"SWAP\"").unwrap();
292 assert_eq!(v, InstType::Swap);
293 assert_eq!(serde_json::to_string(&v).unwrap(), "\"SWAP\"");
294 }
295
296 #[test]
297 fn unknown_enum_value_is_preserved_not_an_error() {
298 let v: InstType = serde_json::from_str("\"FUTURE_THING\"").unwrap();
299 assert_eq!(v, InstType::Unknown("FUTURE_THING".to_owned()));
300 assert_eq!(serde_json::to_string(&v).unwrap(), "\"FUTURE_THING\"");
302 }
303}