1use super::{
6 ClientConfigureRejectTransaction, ClientConfigureTransaction, CloseTransaction,
7 CreateTransaction, DailyFinancingTransaction, DelayedTradeClosureTransaction,
8 DividendAdjustmentTransaction, FixedPriceOrder, FixedPriceOrderTransaction,
9 GuaranteedStopLossOrder, GuaranteedStopLossOrderRejectTransaction,
10 GuaranteedStopLossOrderRequest, GuaranteedStopLossOrderTransaction, LimitOrder,
11 LimitOrderRejectTransaction, LimitOrderRequest, LimitOrderTransaction,
12 MarginCallEnterTransaction, MarginCallExitTransaction, MarginCallExtendTransaction,
13 MarketIfTouchedOrder, MarketIfTouchedOrderRejectTransaction, MarketIfTouchedOrderRequest,
14 MarketIfTouchedOrderTransaction, MarketOrder, MarketOrderRejectTransaction, MarketOrderRequest,
15 MarketOrderTransaction, OrderCancelRejectTransaction, OrderCancelTransaction,
16 OrderClientExtensionsModifyRejectTransaction, OrderClientExtensionsModifyTransaction,
17 OrderFillTransaction, ReopenTransaction, ResetResettablePLTransaction, StopLossOrder,
18 StopLossOrderRejectTransaction, StopLossOrderRequest, StopLossOrderTransaction, StopOrder,
19 StopOrderRejectTransaction, StopOrderRequest, StopOrderTransaction, TakeProfitOrder,
20 TakeProfitOrderRejectTransaction, TakeProfitOrderRequest, TakeProfitOrderTransaction,
21 TradeClientExtensionsModifyRejectTransaction, TradeClientExtensionsModifyTransaction,
22 TrailingStopLossOrder, TrailingStopLossOrderRejectTransaction, TrailingStopLossOrderRequest,
23 TrailingStopLossOrderTransaction, TransferFundsRejectTransaction, TransferFundsTransaction,
24};
25use serde::{Deserialize, Serialize};
26
27#[derive(Debug, Clone, PartialEq)]
30#[non_exhaustive]
31pub enum Order {
32 MarketOrder(MarketOrder),
34 LimitOrder(LimitOrder),
36 StopOrder(StopOrder),
38 MarketIfTouchedOrder(MarketIfTouchedOrder),
40 TakeProfitOrder(TakeProfitOrder),
42 StopLossOrder(StopLossOrder),
44 GuaranteedStopLossOrder(GuaranteedStopLossOrder),
46 TrailingStopLossOrder(TrailingStopLossOrder),
48 FixedPriceOrder(FixedPriceOrder),
50 Unknown {
52 kind: String,
54 raw: serde_json::Value,
56 },
57}
58
59impl<'de> Deserialize<'de> for Order {
60 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
61 let value = serde_json::Value::deserialize(deserializer)?;
62 let kind = discriminator(&value).map_err(serde::de::Error::custom)?;
63 match kind {
64 "MARKET" => serde_json::from_value(value)
65 .map(Self::MarketOrder)
66 .map_err(serde::de::Error::custom),
67 "LIMIT" => serde_json::from_value(value)
68 .map(Self::LimitOrder)
69 .map_err(serde::de::Error::custom),
70 "STOP" => serde_json::from_value(value)
71 .map(Self::StopOrder)
72 .map_err(serde::de::Error::custom),
73 "MARKET_IF_TOUCHED" => serde_json::from_value(value)
74 .map(Self::MarketIfTouchedOrder)
75 .map_err(serde::de::Error::custom),
76 "TAKE_PROFIT" => serde_json::from_value(value)
77 .map(Self::TakeProfitOrder)
78 .map_err(serde::de::Error::custom),
79 "STOP_LOSS" => serde_json::from_value(value)
80 .map(Self::StopLossOrder)
81 .map_err(serde::de::Error::custom),
82 "GUARANTEED_STOP_LOSS" => serde_json::from_value(value)
83 .map(Self::GuaranteedStopLossOrder)
84 .map_err(serde::de::Error::custom),
85 "TRAILING_STOP_LOSS" => serde_json::from_value(value)
86 .map(Self::TrailingStopLossOrder)
87 .map_err(serde::de::Error::custom),
88 "FIXED_PRICE" => serde_json::from_value(value)
89 .map(Self::FixedPriceOrder)
90 .map_err(serde::de::Error::custom),
91 other => Ok(Self::Unknown {
92 kind: other.to_owned(),
93 raw: value,
94 }),
95 }
96 }
97}
98
99impl Serialize for Order {
100 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
101 let (kind, value) = match self {
102 Self::MarketOrder(body) => (
103 "MARKET",
104 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
105 ),
106 Self::LimitOrder(body) => (
107 "LIMIT",
108 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
109 ),
110 Self::StopOrder(body) => (
111 "STOP",
112 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
113 ),
114 Self::MarketIfTouchedOrder(body) => (
115 "MARKET_IF_TOUCHED",
116 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
117 ),
118 Self::TakeProfitOrder(body) => (
119 "TAKE_PROFIT",
120 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
121 ),
122 Self::StopLossOrder(body) => (
123 "STOP_LOSS",
124 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
125 ),
126 Self::GuaranteedStopLossOrder(body) => (
127 "GUARANTEED_STOP_LOSS",
128 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
129 ),
130 Self::TrailingStopLossOrder(body) => (
131 "TRAILING_STOP_LOSS",
132 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
133 ),
134 Self::FixedPriceOrder(body) => (
135 "FIXED_PRICE",
136 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
137 ),
138 Self::Unknown { kind, raw } => (kind.as_str(), raw.clone()),
139 };
140 let value = tagged_value(kind, value).map_err(serde::ser::Error::custom)?;
141 value.serialize(serializer)
142 }
143}
144
145#[derive(Debug, Clone, PartialEq)]
148#[non_exhaustive]
149pub enum OrderRequest {
150 MarketOrderRequest(MarketOrderRequest),
152 LimitOrderRequest(LimitOrderRequest),
154 StopOrderRequest(StopOrderRequest),
156 MarketIfTouchedOrderRequest(MarketIfTouchedOrderRequest),
158 TakeProfitOrderRequest(TakeProfitOrderRequest),
160 StopLossOrderRequest(StopLossOrderRequest),
162 GuaranteedStopLossOrderRequest(GuaranteedStopLossOrderRequest),
164 TrailingStopLossOrderRequest(TrailingStopLossOrderRequest),
166}
167
168impl<'de> Deserialize<'de> for OrderRequest {
169 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
170 let value = serde_json::Value::deserialize(deserializer)?;
171 let kind = discriminator(&value).map_err(serde::de::Error::custom)?;
172 match kind {
173 "MARKET" => serde_json::from_value(value)
174 .map(Self::MarketOrderRequest)
175 .map_err(serde::de::Error::custom),
176 "LIMIT" => serde_json::from_value(value)
177 .map(Self::LimitOrderRequest)
178 .map_err(serde::de::Error::custom),
179 "STOP" => serde_json::from_value(value)
180 .map(Self::StopOrderRequest)
181 .map_err(serde::de::Error::custom),
182 "MARKET_IF_TOUCHED" => serde_json::from_value(value)
183 .map(Self::MarketIfTouchedOrderRequest)
184 .map_err(serde::de::Error::custom),
185 "TAKE_PROFIT" => serde_json::from_value(value)
186 .map(Self::TakeProfitOrderRequest)
187 .map_err(serde::de::Error::custom),
188 "STOP_LOSS" => serde_json::from_value(value)
189 .map(Self::StopLossOrderRequest)
190 .map_err(serde::de::Error::custom),
191 "GUARANTEED_STOP_LOSS" => serde_json::from_value(value)
192 .map(Self::GuaranteedStopLossOrderRequest)
193 .map_err(serde::de::Error::custom),
194 "TRAILING_STOP_LOSS" => serde_json::from_value(value)
195 .map(Self::TrailingStopLossOrderRequest)
196 .map_err(serde::de::Error::custom),
197 other => Err(serde::de::Error::custom(format!(
198 "unsupported OANDA request type {other:?}"
199 ))),
200 }
201 }
202}
203
204impl Serialize for OrderRequest {
205 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
206 let (kind, value) = match self {
207 Self::MarketOrderRequest(body) => (
208 "MARKET",
209 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
210 ),
211 Self::LimitOrderRequest(body) => (
212 "LIMIT",
213 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
214 ),
215 Self::StopOrderRequest(body) => (
216 "STOP",
217 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
218 ),
219 Self::MarketIfTouchedOrderRequest(body) => (
220 "MARKET_IF_TOUCHED",
221 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
222 ),
223 Self::TakeProfitOrderRequest(body) => (
224 "TAKE_PROFIT",
225 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
226 ),
227 Self::StopLossOrderRequest(body) => (
228 "STOP_LOSS",
229 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
230 ),
231 Self::GuaranteedStopLossOrderRequest(body) => (
232 "GUARANTEED_STOP_LOSS",
233 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
234 ),
235 Self::TrailingStopLossOrderRequest(body) => (
236 "TRAILING_STOP_LOSS",
237 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
238 ),
239 };
240 let value = tagged_value(kind, value).map_err(serde::ser::Error::custom)?;
241 value.serialize(serializer)
242 }
243}
244
245#[derive(Debug, Clone, PartialEq)]
248#[non_exhaustive]
249#[expect(
250 clippy::large_enum_variant,
251 reason = "boxing a variant would change every caller's match; the stream already boxes the whole Transaction"
252)]
253pub enum Transaction {
254 CreateTransaction(CreateTransaction),
256 CloseTransaction(CloseTransaction),
258 ReopenTransaction(ReopenTransaction),
260 ClientConfigureTransaction(ClientConfigureTransaction),
262 ClientConfigureRejectTransaction(ClientConfigureRejectTransaction),
264 TransferFundsTransaction(TransferFundsTransaction),
266 TransferFundsRejectTransaction(TransferFundsRejectTransaction),
268 MarketOrderTransaction(MarketOrderTransaction),
270 MarketOrderRejectTransaction(MarketOrderRejectTransaction),
272 FixedPriceOrderTransaction(FixedPriceOrderTransaction),
274 LimitOrderTransaction(LimitOrderTransaction),
276 LimitOrderRejectTransaction(LimitOrderRejectTransaction),
278 StopOrderTransaction(StopOrderTransaction),
280 StopOrderRejectTransaction(StopOrderRejectTransaction),
282 MarketIfTouchedOrderTransaction(MarketIfTouchedOrderTransaction),
284 MarketIfTouchedOrderRejectTransaction(MarketIfTouchedOrderRejectTransaction),
286 TakeProfitOrderTransaction(TakeProfitOrderTransaction),
288 TakeProfitOrderRejectTransaction(TakeProfitOrderRejectTransaction),
290 StopLossOrderTransaction(StopLossOrderTransaction),
292 StopLossOrderRejectTransaction(StopLossOrderRejectTransaction),
294 GuaranteedStopLossOrderTransaction(GuaranteedStopLossOrderTransaction),
296 GuaranteedStopLossOrderRejectTransaction(GuaranteedStopLossOrderRejectTransaction),
298 TrailingStopLossOrderTransaction(TrailingStopLossOrderTransaction),
300 TrailingStopLossOrderRejectTransaction(TrailingStopLossOrderRejectTransaction),
302 OrderFillTransaction(OrderFillTransaction),
304 OrderCancelTransaction(OrderCancelTransaction),
306 OrderCancelRejectTransaction(OrderCancelRejectTransaction),
308 OrderClientExtensionsModifyTransaction(OrderClientExtensionsModifyTransaction),
310 OrderClientExtensionsModifyRejectTransaction(OrderClientExtensionsModifyRejectTransaction),
312 TradeClientExtensionsModifyTransaction(TradeClientExtensionsModifyTransaction),
314 TradeClientExtensionsModifyRejectTransaction(TradeClientExtensionsModifyRejectTransaction),
316 MarginCallEnterTransaction(MarginCallEnterTransaction),
318 MarginCallExtendTransaction(MarginCallExtendTransaction),
320 MarginCallExitTransaction(MarginCallExitTransaction),
322 DelayedTradeClosureTransaction(DelayedTradeClosureTransaction),
324 DailyFinancingTransaction(DailyFinancingTransaction),
326 DividendAdjustmentTransaction(DividendAdjustmentTransaction),
328 ResetResettablePLTransaction(ResetResettablePLTransaction),
330 Unknown {
332 kind: String,
334 raw: serde_json::Value,
336 },
337}
338
339impl<'de> Deserialize<'de> for Transaction {
340 #[expect(
341 clippy::too_many_lines,
342 reason = "the exhaustive provider discriminator table keeps response decoding auditable"
343 )]
344 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
345 let value = serde_json::Value::deserialize(deserializer)?;
346 let kind = discriminator(&value).map_err(serde::de::Error::custom)?;
347 match kind {
348 "CREATE" => serde_json::from_value(value)
349 .map(Self::CreateTransaction)
350 .map_err(serde::de::Error::custom),
351 "CLOSE" => serde_json::from_value(value)
352 .map(Self::CloseTransaction)
353 .map_err(serde::de::Error::custom),
354 "REOPEN" => serde_json::from_value(value)
355 .map(Self::ReopenTransaction)
356 .map_err(serde::de::Error::custom),
357 "CLIENT_CONFIGURE" => serde_json::from_value(value)
358 .map(Self::ClientConfigureTransaction)
359 .map_err(serde::de::Error::custom),
360 "CLIENT_CONFIGURE_REJECT" => serde_json::from_value(value)
361 .map(Self::ClientConfigureRejectTransaction)
362 .map_err(serde::de::Error::custom),
363 "TRANSFER_FUNDS" => serde_json::from_value(value)
364 .map(Self::TransferFundsTransaction)
365 .map_err(serde::de::Error::custom),
366 "TRANSFER_FUNDS_REJECT" => serde_json::from_value(value)
367 .map(Self::TransferFundsRejectTransaction)
368 .map_err(serde::de::Error::custom),
369 "MARKET_ORDER" => serde_json::from_value(value)
370 .map(Self::MarketOrderTransaction)
371 .map_err(serde::de::Error::custom),
372 "MARKET_ORDER_REJECT" => serde_json::from_value(value)
373 .map(Self::MarketOrderRejectTransaction)
374 .map_err(serde::de::Error::custom),
375 "FIXED_PRICE_ORDER" => serde_json::from_value(value)
376 .map(Self::FixedPriceOrderTransaction)
377 .map_err(serde::de::Error::custom),
378 "LIMIT_ORDER" => serde_json::from_value(value)
379 .map(Self::LimitOrderTransaction)
380 .map_err(serde::de::Error::custom),
381 "LIMIT_ORDER_REJECT" => serde_json::from_value(value)
382 .map(Self::LimitOrderRejectTransaction)
383 .map_err(serde::de::Error::custom),
384 "STOP_ORDER" => serde_json::from_value(value)
385 .map(Self::StopOrderTransaction)
386 .map_err(serde::de::Error::custom),
387 "STOP_ORDER_REJECT" => serde_json::from_value(value)
388 .map(Self::StopOrderRejectTransaction)
389 .map_err(serde::de::Error::custom),
390 "MARKET_IF_TOUCHED_ORDER" => serde_json::from_value(value)
391 .map(Self::MarketIfTouchedOrderTransaction)
392 .map_err(serde::de::Error::custom),
393 "MARKET_IF_TOUCHED_ORDER_REJECT" => serde_json::from_value(value)
394 .map(Self::MarketIfTouchedOrderRejectTransaction)
395 .map_err(serde::de::Error::custom),
396 "TAKE_PROFIT_ORDER" => serde_json::from_value(value)
397 .map(Self::TakeProfitOrderTransaction)
398 .map_err(serde::de::Error::custom),
399 "TAKE_PROFIT_ORDER_REJECT" => serde_json::from_value(value)
400 .map(Self::TakeProfitOrderRejectTransaction)
401 .map_err(serde::de::Error::custom),
402 "STOP_LOSS_ORDER" => serde_json::from_value(value)
403 .map(Self::StopLossOrderTransaction)
404 .map_err(serde::de::Error::custom),
405 "STOP_LOSS_ORDER_REJECT" => serde_json::from_value(value)
406 .map(Self::StopLossOrderRejectTransaction)
407 .map_err(serde::de::Error::custom),
408 "GUARANTEED_STOP_LOSS_ORDER" => serde_json::from_value(value)
409 .map(Self::GuaranteedStopLossOrderTransaction)
410 .map_err(serde::de::Error::custom),
411 "GUARANTEED_STOP_LOSS_ORDER_REJECT" => serde_json::from_value(value)
412 .map(Self::GuaranteedStopLossOrderRejectTransaction)
413 .map_err(serde::de::Error::custom),
414 "TRAILING_STOP_LOSS_ORDER" => serde_json::from_value(value)
415 .map(Self::TrailingStopLossOrderTransaction)
416 .map_err(serde::de::Error::custom),
417 "TRAILING_STOP_LOSS_ORDER_REJECT" => serde_json::from_value(value)
418 .map(Self::TrailingStopLossOrderRejectTransaction)
419 .map_err(serde::de::Error::custom),
420 "ORDER_FILL" => serde_json::from_value(value)
421 .map(Self::OrderFillTransaction)
422 .map_err(serde::de::Error::custom),
423 "ORDER_CANCEL" => serde_json::from_value(value)
424 .map(Self::OrderCancelTransaction)
425 .map_err(serde::de::Error::custom),
426 "ORDER_CANCEL_REJECT" => serde_json::from_value(value)
427 .map(Self::OrderCancelRejectTransaction)
428 .map_err(serde::de::Error::custom),
429 "ORDER_CLIENT_EXTENSIONS_MODIFY" => serde_json::from_value(value)
430 .map(Self::OrderClientExtensionsModifyTransaction)
431 .map_err(serde::de::Error::custom),
432 "ORDER_CLIENT_EXTENSIONS_MODIFY_REJECT" => serde_json::from_value(value)
433 .map(Self::OrderClientExtensionsModifyRejectTransaction)
434 .map_err(serde::de::Error::custom),
435 "TRADE_CLIENT_EXTENSIONS_MODIFY" => serde_json::from_value(value)
436 .map(Self::TradeClientExtensionsModifyTransaction)
437 .map_err(serde::de::Error::custom),
438 "TRADE_CLIENT_EXTENSIONS_MODIFY_REJECT" => serde_json::from_value(value)
439 .map(Self::TradeClientExtensionsModifyRejectTransaction)
440 .map_err(serde::de::Error::custom),
441 "MARGIN_CALL_ENTER" => serde_json::from_value(value)
442 .map(Self::MarginCallEnterTransaction)
443 .map_err(serde::de::Error::custom),
444 "MARGIN_CALL_EXTEND" => serde_json::from_value(value)
445 .map(Self::MarginCallExtendTransaction)
446 .map_err(serde::de::Error::custom),
447 "MARGIN_CALL_EXIT" => serde_json::from_value(value)
448 .map(Self::MarginCallExitTransaction)
449 .map_err(serde::de::Error::custom),
450 "DELAYED_TRADE_CLOSURE" => serde_json::from_value(value)
451 .map(Self::DelayedTradeClosureTransaction)
452 .map_err(serde::de::Error::custom),
453 "DAILY_FINANCING" => serde_json::from_value(value)
454 .map(Self::DailyFinancingTransaction)
455 .map_err(serde::de::Error::custom),
456 "DIVIDEND_ADJUSTMENT" => serde_json::from_value(value)
457 .map(Self::DividendAdjustmentTransaction)
458 .map_err(serde::de::Error::custom),
459 "RESET_RESETTABLE_PL" => serde_json::from_value(value)
460 .map(Self::ResetResettablePLTransaction)
461 .map_err(serde::de::Error::custom),
462 other => Ok(Self::Unknown {
463 kind: other.to_owned(),
464 raw: value,
465 }),
466 }
467 }
468}
469
470impl Serialize for Transaction {
471 #[expect(
472 clippy::too_many_lines,
473 reason = "the exhaustive provider discriminator table keeps response serialization auditable"
474 )]
475 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
476 let (kind, value) = match self {
477 Self::CreateTransaction(body) => (
478 "CREATE",
479 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
480 ),
481 Self::CloseTransaction(body) => (
482 "CLOSE",
483 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
484 ),
485 Self::ReopenTransaction(body) => (
486 "REOPEN",
487 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
488 ),
489 Self::ClientConfigureTransaction(body) => (
490 "CLIENT_CONFIGURE",
491 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
492 ),
493 Self::ClientConfigureRejectTransaction(body) => (
494 "CLIENT_CONFIGURE_REJECT",
495 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
496 ),
497 Self::TransferFundsTransaction(body) => (
498 "TRANSFER_FUNDS",
499 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
500 ),
501 Self::TransferFundsRejectTransaction(body) => (
502 "TRANSFER_FUNDS_REJECT",
503 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
504 ),
505 Self::MarketOrderTransaction(body) => (
506 "MARKET_ORDER",
507 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
508 ),
509 Self::MarketOrderRejectTransaction(body) => (
510 "MARKET_ORDER_REJECT",
511 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
512 ),
513 Self::FixedPriceOrderTransaction(body) => (
514 "FIXED_PRICE_ORDER",
515 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
516 ),
517 Self::LimitOrderTransaction(body) => (
518 "LIMIT_ORDER",
519 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
520 ),
521 Self::LimitOrderRejectTransaction(body) => (
522 "LIMIT_ORDER_REJECT",
523 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
524 ),
525 Self::StopOrderTransaction(body) => (
526 "STOP_ORDER",
527 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
528 ),
529 Self::StopOrderRejectTransaction(body) => (
530 "STOP_ORDER_REJECT",
531 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
532 ),
533 Self::MarketIfTouchedOrderTransaction(body) => (
534 "MARKET_IF_TOUCHED_ORDER",
535 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
536 ),
537 Self::MarketIfTouchedOrderRejectTransaction(body) => (
538 "MARKET_IF_TOUCHED_ORDER_REJECT",
539 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
540 ),
541 Self::TakeProfitOrderTransaction(body) => (
542 "TAKE_PROFIT_ORDER",
543 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
544 ),
545 Self::TakeProfitOrderRejectTransaction(body) => (
546 "TAKE_PROFIT_ORDER_REJECT",
547 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
548 ),
549 Self::StopLossOrderTransaction(body) => (
550 "STOP_LOSS_ORDER",
551 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
552 ),
553 Self::StopLossOrderRejectTransaction(body) => (
554 "STOP_LOSS_ORDER_REJECT",
555 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
556 ),
557 Self::GuaranteedStopLossOrderTransaction(body) => (
558 "GUARANTEED_STOP_LOSS_ORDER",
559 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
560 ),
561 Self::GuaranteedStopLossOrderRejectTransaction(body) => (
562 "GUARANTEED_STOP_LOSS_ORDER_REJECT",
563 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
564 ),
565 Self::TrailingStopLossOrderTransaction(body) => (
566 "TRAILING_STOP_LOSS_ORDER",
567 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
568 ),
569 Self::TrailingStopLossOrderRejectTransaction(body) => (
570 "TRAILING_STOP_LOSS_ORDER_REJECT",
571 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
572 ),
573 Self::OrderFillTransaction(body) => (
574 "ORDER_FILL",
575 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
576 ),
577 Self::OrderCancelTransaction(body) => (
578 "ORDER_CANCEL",
579 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
580 ),
581 Self::OrderCancelRejectTransaction(body) => (
582 "ORDER_CANCEL_REJECT",
583 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
584 ),
585 Self::OrderClientExtensionsModifyTransaction(body) => (
586 "ORDER_CLIENT_EXTENSIONS_MODIFY",
587 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
588 ),
589 Self::OrderClientExtensionsModifyRejectTransaction(body) => (
590 "ORDER_CLIENT_EXTENSIONS_MODIFY_REJECT",
591 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
592 ),
593 Self::TradeClientExtensionsModifyTransaction(body) => (
594 "TRADE_CLIENT_EXTENSIONS_MODIFY",
595 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
596 ),
597 Self::TradeClientExtensionsModifyRejectTransaction(body) => (
598 "TRADE_CLIENT_EXTENSIONS_MODIFY_REJECT",
599 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
600 ),
601 Self::MarginCallEnterTransaction(body) => (
602 "MARGIN_CALL_ENTER",
603 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
604 ),
605 Self::MarginCallExtendTransaction(body) => (
606 "MARGIN_CALL_EXTEND",
607 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
608 ),
609 Self::MarginCallExitTransaction(body) => (
610 "MARGIN_CALL_EXIT",
611 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
612 ),
613 Self::DelayedTradeClosureTransaction(body) => (
614 "DELAYED_TRADE_CLOSURE",
615 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
616 ),
617 Self::DailyFinancingTransaction(body) => (
618 "DAILY_FINANCING",
619 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
620 ),
621 Self::DividendAdjustmentTransaction(body) => (
622 "DIVIDEND_ADJUSTMENT",
623 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
624 ),
625 Self::ResetResettablePLTransaction(body) => (
626 "RESET_RESETTABLE_PL",
627 serde_json::to_value(body).map_err(serde::ser::Error::custom)?,
628 ),
629 Self::Unknown { kind, raw } => (kind.as_str(), raw.clone()),
630 };
631 let value = tagged_value(kind, value).map_err(serde::ser::Error::custom)?;
632 value.serialize(serializer)
633 }
634}
635
636fn discriminator(value: &serde_json::Value) -> Result<&str, String> {
637 match value.get("type") {
638 None => Err("missing OANDA type discriminator".to_owned()),
639 Some(serde_json::Value::String(kind)) => Ok(kind),
640 Some(observed) => Err(format!(
641 "OANDA type discriminator must be a string; observed {observed}"
642 )),
643 }
644}
645
646fn tagged_value(kind: &str, value: serde_json::Value) -> Result<serde_json::Value, String> {
647 let value = {
648 let mut value = value;
649 let object = value
650 .as_object_mut()
651 .ok_or_else(|| "OANDA variant must be an object".to_owned())?;
652 if let Some(observed) = object.get("type")
653 && observed.as_str() != Some(kind)
654 {
655 return Err(format!(
656 "OANDA type discriminator mismatch: expected {kind:?}, observed {observed}"
657 ));
658 }
659 object.insert(
660 "type".to_owned(),
661 serde_json::Value::String(kind.to_owned()),
662 );
663 value
664 };
665 Ok(value)
666}
667
668impl crate::validation::Validate for OrderRequest {
669 fn validate(&self) -> crate::Result<()> {
670 match self {
671 Self::MarketOrderRequest(body) => body.validate(),
672 Self::LimitOrderRequest(body) => body.validate(),
673 Self::StopOrderRequest(body) => body.validate(),
674 Self::MarketIfTouchedOrderRequest(body) => body.validate(),
675 Self::TakeProfitOrderRequest(body) => body.validate(),
676 Self::StopLossOrderRequest(body) => body.validate(),
677 Self::GuaranteedStopLossOrderRequest(body) => body.validate(),
678 Self::TrailingStopLossOrderRequest(body) => body.validate(),
679 }
680 }
681}