1use diesel::deserialize::{self, FromSql};
8use diesel::pg::{Pg, PgValue};
9use diesel::serialize::{self, IsNull, Output, ToSql};
10use diesel::sql_types::Uuid as DieselUuid;
11use diesel::{AsExpression, FromSqlRow, QueryId, SqlType};
12use serde::{Deserialize, Serialize};
13use std::io::Write;
14
15#[derive(SqlType, QueryId, Debug)]
26#[diesel(postgres_type(name = "engine_command_type"))]
27pub struct EngineCommandTypeSql;
28
29#[derive(SqlType, QueryId, Debug)]
31#[diesel(postgres_type(name = "engine_event_type"))]
32pub struct EngineEventTypeSql;
33
34#[derive(
40 Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, AsExpression, FromSqlRow,
41)]
42#[diesel(sql_type = EngineCommandTypeSql)]
43pub enum CommandType {
44 CreateOrder,
45 CancelOrder,
46 ReplaceOrder,
47 CreateMarket,
48 DeleteMarket,
49 ExpireMarket,
50 LiquidationState,
51 TickExpiry,
52 TickSnapshot,
53 RfqExecute,
54 PriceUpdate,
55 IvUpdate,
56 TierUpdate,
57 HypercorePositionUpdate,
58 MmpConfigUpdate,
59 TradingModeUpdate,
60 DepositUpdate,
61 LiquidationBonusUpdate,
62 ApproveAgent,
63 RevokeAgent,
64 NonceAdvance,
65 HypercoreEquityUpdate,
66 OptionDepositUpdate,
67 OptionWithdrawalUpdate,
68 CashWithdrawalUpdate,
69 SetPmSettlementPoolConfig,
70 RecordPmVaultDeposit,
71 RequestPmVaultWithdrawal,
72 AccruePmSettlementInterest,
73 ApplyPmSettlementRepayment,
74 JournalPmRecoveryPlan,
75 MarkPmRecoveryActionSubmitted,
76 ResolvePmRecoveryAction,
77}
78
79impl CommandType {
80 pub fn from_str(s: &str) -> Option<Self> {
82 match s {
83 "CreateOrder" => Some(CommandType::CreateOrder),
84 "CancelOrder" => Some(CommandType::CancelOrder),
85 "ReplaceOrder" => Some(CommandType::ReplaceOrder),
86 "CreateMarket" => Some(CommandType::CreateMarket),
87 "DeleteMarket" => Some(CommandType::DeleteMarket),
88 "ExpireMarket" => Some(CommandType::ExpireMarket),
89 "LiquidationState" => Some(CommandType::LiquidationState),
90 "TickExpiry" => Some(CommandType::TickExpiry),
91 "TickSnapshot" => Some(CommandType::TickSnapshot),
92 "RfqExecute" => Some(CommandType::RfqExecute),
93 "PriceUpdate" => Some(CommandType::PriceUpdate),
94 "IvUpdate" => Some(CommandType::IvUpdate),
95 "TierUpdate" => Some(CommandType::TierUpdate),
96 "HypercorePositionUpdate" => Some(CommandType::HypercorePositionUpdate),
97 "MmpConfigUpdate" => Some(CommandType::MmpConfigUpdate),
98 "TradingModeUpdate" => Some(CommandType::TradingModeUpdate),
99 "DepositUpdate" => Some(CommandType::DepositUpdate),
100 "LiquidationBonusUpdate" => Some(CommandType::LiquidationBonusUpdate),
101 "ApproveAgent" => Some(CommandType::ApproveAgent),
102 "RevokeAgent" => Some(CommandType::RevokeAgent),
103 "NonceAdvance" => Some(CommandType::NonceAdvance),
104 "HypercoreEquityUpdate" => Some(CommandType::HypercoreEquityUpdate),
105 "OptionDepositUpdate" => Some(CommandType::OptionDepositUpdate),
106 "OptionWithdrawalUpdate" => Some(CommandType::OptionWithdrawalUpdate),
107 "CashWithdrawalUpdate" => Some(CommandType::CashWithdrawalUpdate),
108 "SetPmSettlementPoolConfig" => Some(CommandType::SetPmSettlementPoolConfig),
109 "RecordPmVaultDeposit" => Some(CommandType::RecordPmVaultDeposit),
110 "RequestPmVaultWithdrawal" => Some(CommandType::RequestPmVaultWithdrawal),
111 "AccruePmSettlementInterest" => Some(CommandType::AccruePmSettlementInterest),
112 "ApplyPmSettlementRepayment" => Some(CommandType::ApplyPmSettlementRepayment),
113 "JournalPmRecoveryPlan" => Some(CommandType::JournalPmRecoveryPlan),
114 "MarkPmRecoveryActionSubmitted" => Some(CommandType::MarkPmRecoveryActionSubmitted),
115 "ResolvePmRecoveryAction" => Some(CommandType::ResolvePmRecoveryAction),
116 _ => None,
117 }
118 }
119
120 pub fn as_str(&self) -> &'static str {
122 match self {
123 CommandType::CreateOrder => "CreateOrder",
124 CommandType::CancelOrder => "CancelOrder",
125 CommandType::ReplaceOrder => "ReplaceOrder",
126 CommandType::CreateMarket => "CreateMarket",
127 CommandType::DeleteMarket => "DeleteMarket",
128 CommandType::ExpireMarket => "ExpireMarket",
129 CommandType::LiquidationState => "LiquidationState",
130 CommandType::TickExpiry => "TickExpiry",
131 CommandType::TickSnapshot => "TickSnapshot",
132 CommandType::RfqExecute => "RfqExecute",
133 CommandType::PriceUpdate => "PriceUpdate",
134 CommandType::IvUpdate => "IvUpdate",
135 CommandType::TierUpdate => "TierUpdate",
136 CommandType::HypercorePositionUpdate => "HypercorePositionUpdate",
137 CommandType::MmpConfigUpdate => "MmpConfigUpdate",
138 CommandType::TradingModeUpdate => "TradingModeUpdate",
139 CommandType::DepositUpdate => "DepositUpdate",
140 CommandType::LiquidationBonusUpdate => "LiquidationBonusUpdate",
141 CommandType::ApproveAgent => "ApproveAgent",
142 CommandType::RevokeAgent => "RevokeAgent",
143 CommandType::NonceAdvance => "NonceAdvance",
144 CommandType::HypercoreEquityUpdate => "HypercoreEquityUpdate",
145 CommandType::OptionDepositUpdate => "OptionDepositUpdate",
146 CommandType::OptionWithdrawalUpdate => "OptionWithdrawalUpdate",
147 CommandType::CashWithdrawalUpdate => "CashWithdrawalUpdate",
148 CommandType::SetPmSettlementPoolConfig => "SetPmSettlementPoolConfig",
149 CommandType::RecordPmVaultDeposit => "RecordPmVaultDeposit",
150 CommandType::RequestPmVaultWithdrawal => "RequestPmVaultWithdrawal",
151 CommandType::AccruePmSettlementInterest => "AccruePmSettlementInterest",
152 CommandType::ApplyPmSettlementRepayment => "ApplyPmSettlementRepayment",
153 CommandType::JournalPmRecoveryPlan => "JournalPmRecoveryPlan",
154 CommandType::MarkPmRecoveryActionSubmitted => "MarkPmRecoveryActionSubmitted",
155 CommandType::ResolvePmRecoveryAction => "ResolvePmRecoveryAction",
156 }
157 }
158}
159
160impl std::fmt::Display for CommandType {
161 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
162 f.write_str(self.as_str())
163 }
164}
165
166impl ToSql<EngineCommandTypeSql, Pg> for CommandType {
167 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
168 out.write_all(self.as_str().as_bytes())?;
169 Ok(IsNull::No)
170 }
171}
172
173impl FromSql<EngineCommandTypeSql, Pg> for CommandType {
174 fn from_sql(bytes: PgValue<'_>) -> deserialize::Result<Self> {
175 let s = std::str::from_utf8(bytes.as_bytes())
176 .expect("engine_command_type column contains invalid UTF-8");
177 Ok(CommandType::from_str(s).unwrap_or_else(|| {
178 panic!(
179 "Persisted engine_command_type '{}' is not a known variant. \
180 This indicates data corruption or a missing enum variant.",
181 s
182 )
183 }))
184 }
185}
186
187#[derive(
193 Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, AsExpression, FromSqlRow,
194)]
195#[diesel(sql_type = EngineEventTypeSql)]
196pub enum EventType {
197 OrderAction,
198 OrderUpdate,
199 OrderInfo,
200 MarketAction,
201 MarketUpdate,
202 OrderFilled,
203 OrderbookUpdated,
204 L2Update,
205 Trade,
206 TransactionRequest,
207 TransactionUpdate,
208 MmpTriggered,
209 PositionExpired,
210 TierUpdate,
211 HypercorePositionUpdate,
212 LiquidationStateChange,
213 RfqFilled,
214}
215
216impl EventType {
217 pub fn from_str(s: &str) -> Option<Self> {
219 match s {
220 "OrderAction" => Some(EventType::OrderAction),
221 "OrderUpdate" => Some(EventType::OrderUpdate),
222 "OrderInfo" => Some(EventType::OrderInfo),
223 "MarketAction" => Some(EventType::MarketAction),
224 "MarketUpdate" => Some(EventType::MarketUpdate),
225 "OrderFilled" => Some(EventType::OrderFilled),
226 "OrderbookUpdated" => Some(EventType::OrderbookUpdated),
227 "L2Update" => Some(EventType::L2Update),
228 "Trade" => Some(EventType::Trade),
229 "TransactionRequest" => Some(EventType::TransactionRequest),
230 "TransactionUpdate" => Some(EventType::TransactionUpdate),
231 "MmpTriggered" => Some(EventType::MmpTriggered),
232 "PositionExpired" => Some(EventType::PositionExpired),
233 "TierUpdate" => Some(EventType::TierUpdate),
234 "HypercorePositionUpdate" => Some(EventType::HypercorePositionUpdate),
235 "LiquidationStateChange" => Some(EventType::LiquidationStateChange),
236 "RfqFilled" => Some(EventType::RfqFilled),
237 _ => None,
238 }
239 }
240
241 pub fn as_str(&self) -> &'static str {
243 match self {
244 EventType::OrderAction => "OrderAction",
245 EventType::OrderUpdate => "OrderUpdate",
246 EventType::OrderInfo => "OrderInfo",
247 EventType::MarketAction => "MarketAction",
248 EventType::MarketUpdate => "MarketUpdate",
249 EventType::OrderFilled => "OrderFilled",
250 EventType::OrderbookUpdated => "OrderbookUpdated",
251 EventType::L2Update => "L2Update",
252 EventType::Trade => "Trade",
253 EventType::TransactionRequest => "TransactionRequest",
254 EventType::TransactionUpdate => "TransactionUpdate",
255 EventType::MmpTriggered => "MmpTriggered",
256 EventType::PositionExpired => "PositionExpired",
257 EventType::TierUpdate => "TierUpdate",
258 EventType::HypercorePositionUpdate => "HypercorePositionUpdate",
259 EventType::LiquidationStateChange => "LiquidationStateChange",
260 EventType::RfqFilled => "RfqFilled",
261 }
262 }
263}
264
265impl std::fmt::Display for EventType {
266 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
267 f.write_str(self.as_str())
268 }
269}
270
271impl From<EventType> for hypercall_db::EventType {
272 fn from(e: EventType) -> Self {
273 e.as_str().parse().expect("EventType variant mismatch")
274 }
275}
276
277impl ToSql<EngineEventTypeSql, Pg> for EventType {
278 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
279 out.write_all(self.as_str().as_bytes())?;
280 Ok(IsNull::No)
281 }
282}
283
284impl FromSql<EngineEventTypeSql, Pg> for EventType {
285 fn from_sql(bytes: PgValue<'_>) -> deserialize::Result<Self> {
286 let s = std::str::from_utf8(bytes.as_bytes())
287 .expect("engine_event_type column contains invalid UTF-8");
288 Ok(EventType::from_str(s).unwrap_or_else(|| {
289 panic!(
290 "Persisted engine_event_type '{}' is not a known variant. \
291 This indicates data corruption or a missing enum variant.",
292 s
293 )
294 }))
295 }
296}
297
298#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, AsExpression, FromSqlRow)]
309#[diesel(sql_type = DieselUuid)]
310pub struct DbUuid(pub uuid::Uuid);
311
312impl From<uuid::Uuid> for DbUuid {
313 fn from(u: uuid::Uuid) -> Self {
314 DbUuid(u)
315 }
316}
317
318impl From<DbUuid> for uuid::Uuid {
319 fn from(u: DbUuid) -> Self {
320 u.0
321 }
322}
323
324impl std::fmt::Display for DbUuid {
325 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
326 self.0.fmt(f)
327 }
328}
329
330impl Serialize for DbUuid {
331 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
332 self.0.serialize(serializer)
333 }
334}
335
336impl<'de> Deserialize<'de> for DbUuid {
337 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
338 uuid::Uuid::deserialize(deserializer).map(DbUuid)
339 }
340}
341
342impl ToSql<DieselUuid, Pg> for DbUuid {
343 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
344 out.write_all(self.0.as_bytes())?;
345 Ok(IsNull::No)
346 }
347}
348
349impl FromSql<DieselUuid, Pg> for DbUuid {
350 fn from_sql(bytes: PgValue<'_>) -> deserialize::Result<Self> {
351 let raw = bytes.as_bytes();
352 assert!(
353 raw.len() == 16,
354 "Persisted UUID has {} bytes, expected 16. Data corruption suspected.",
355 raw.len()
356 );
357 Ok(DbUuid(uuid::Uuid::from_slice(raw).unwrap_or_else(|e| {
358 panic!("Persisted UUID bytes are invalid: {}", e)
359 })))
360 }
361}
362
363#[derive(SqlType, QueryId, Debug)]
369#[diesel(postgres_type(name = "directive_action_key"))]
370pub struct DirectiveActionKeySql;
371
372#[derive(
377 Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, AsExpression, FromSqlRow,
378)]
379#[diesel(sql_type = DirectiveActionKeySql)]
380pub enum DirectiveActionKeyDb {
381 HlLimitOrder,
382 HlCancelByOid,
383 HlCancelByCloid,
384 HcUpdateApiWallet,
385 HlSendAsset,
386 HcTransferOption,
387 RsmHlLimitOrder,
388 RsmHlCancelByOid,
389 RsmHlCancelByCloid,
390 RsmHlSendAsset,
391 SystemCreditToken,
392 SystemCreditOption,
393 SystemStartLiquidation,
394 SystemStopLiquidation,
395 SystemWithdrawToken,
396}
397
398impl DirectiveActionKeyDb {
399 pub fn as_str(&self) -> &'static str {
401 hypercall_types::directives::ActionKey::from(*self).as_str()
402 }
403
404 pub fn from_str(s: &str) -> Option<Self> {
406 hypercall_types::directives::ActionKey::parse(s)
407 .ok()
408 .map(Self::from)
409 }
410}
411
412impl std::fmt::Display for DirectiveActionKeyDb {
413 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
414 f.write_str(self.as_str())
415 }
416}
417
418impl From<hypercall_types::directives::ActionKey> for DirectiveActionKeyDb {
419 fn from(key: hypercall_types::directives::ActionKey) -> Self {
420 use hypercall_types::directives::ActionKey as AK;
421 match key {
422 AK::HlLimitOrder => Self::HlLimitOrder,
423 AK::HlCancelByOid => Self::HlCancelByOid,
424 AK::HlCancelByCloid => Self::HlCancelByCloid,
425 AK::HcUpdateApiWallet => Self::HcUpdateApiWallet,
426 AK::HlSendAsset => Self::HlSendAsset,
427 AK::HcTransferOption => Self::HcTransferOption,
428 AK::RsmHlLimitOrder => Self::RsmHlLimitOrder,
429 AK::RsmHlCancelByOid => Self::RsmHlCancelByOid,
430 AK::RsmHlCancelByCloid => Self::RsmHlCancelByCloid,
431 AK::RsmHlSendAsset => Self::RsmHlSendAsset,
432 AK::SystemCreditToken => Self::SystemCreditToken,
433 AK::SystemCreditOption => Self::SystemCreditOption,
434 AK::SystemStartLiquidation => Self::SystemStartLiquidation,
435 AK::SystemStopLiquidation => Self::SystemStopLiquidation,
436 AK::SystemWithdrawToken => Self::SystemWithdrawToken,
437 _ => {
439 panic!(
440 "Unknown ActionKey variant: {}. \
441 Add it to DirectiveActionKeyDb.",
442 key.as_str()
443 )
444 }
445 }
446 }
447}
448
449impl From<DirectiveActionKeyDb> for hypercall_types::directives::ActionKey {
450 fn from(key: DirectiveActionKeyDb) -> Self {
451 use DirectiveActionKeyDb as DB;
452 match key {
453 DB::HlLimitOrder => Self::HlLimitOrder,
454 DB::HlCancelByOid => Self::HlCancelByOid,
455 DB::HlCancelByCloid => Self::HlCancelByCloid,
456 DB::HcUpdateApiWallet => Self::HcUpdateApiWallet,
457 DB::HlSendAsset => Self::HlSendAsset,
458 DB::HcTransferOption => Self::HcTransferOption,
459 DB::RsmHlLimitOrder => Self::RsmHlLimitOrder,
460 DB::RsmHlCancelByOid => Self::RsmHlCancelByOid,
461 DB::RsmHlCancelByCloid => Self::RsmHlCancelByCloid,
462 DB::RsmHlSendAsset => Self::RsmHlSendAsset,
463 DB::SystemCreditToken => Self::SystemCreditToken,
464 DB::SystemCreditOption => Self::SystemCreditOption,
465 DB::SystemStartLiquidation => Self::SystemStartLiquidation,
466 DB::SystemStopLiquidation => Self::SystemStopLiquidation,
467 DB::SystemWithdrawToken => Self::SystemWithdrawToken,
468 }
469 }
470}
471
472impl ToSql<DirectiveActionKeySql, Pg> for DirectiveActionKeyDb {
473 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
474 out.write_all(self.as_str().as_bytes())?;
475 Ok(IsNull::No)
476 }
477}
478
479impl FromSql<DirectiveActionKeySql, Pg> for DirectiveActionKeyDb {
480 fn from_sql(bytes: PgValue<'_>) -> deserialize::Result<Self> {
481 let s = std::str::from_utf8(bytes.as_bytes())
482 .expect("directive_action_key column contains invalid UTF-8");
483 Ok(DirectiveActionKeyDb::from_str(s).unwrap_or_else(|| {
484 panic!(
485 "Persisted directive_action_key '{}' is not a known variant. \
486 This indicates data corruption or a missing enum variant.",
487 s
488 )
489 }))
490 }
491}
492
493#[cfg(test)]
494mod tests {
495 use super::*;
496
497 #[test]
498 fn test_command_type_roundtrip() {
499 for ct in &[
500 CommandType::CreateOrder,
501 CommandType::CancelOrder,
502 CommandType::ReplaceOrder,
503 CommandType::CreateMarket,
504 CommandType::DeleteMarket,
505 CommandType::ExpireMarket,
506 CommandType::LiquidationState,
507 CommandType::TickExpiry,
508 CommandType::TickSnapshot,
509 CommandType::RfqExecute,
510 CommandType::PriceUpdate,
511 CommandType::IvUpdate,
512 CommandType::TierUpdate,
513 CommandType::HypercorePositionUpdate,
514 CommandType::MmpConfigUpdate,
515 CommandType::TradingModeUpdate,
516 CommandType::DepositUpdate,
517 CommandType::LiquidationBonusUpdate,
518 CommandType::ApproveAgent,
519 CommandType::RevokeAgent,
520 CommandType::NonceAdvance,
521 CommandType::HypercoreEquityUpdate,
522 CommandType::OptionDepositUpdate,
523 CommandType::OptionWithdrawalUpdate,
524 CommandType::CashWithdrawalUpdate,
525 CommandType::SetPmSettlementPoolConfig,
526 CommandType::RecordPmVaultDeposit,
527 CommandType::RequestPmVaultWithdrawal,
528 CommandType::AccruePmSettlementInterest,
529 CommandType::ApplyPmSettlementRepayment,
530 CommandType::JournalPmRecoveryPlan,
531 CommandType::MarkPmRecoveryActionSubmitted,
532 CommandType::ResolvePmRecoveryAction,
533 ] {
534 let s = ct.as_str();
535 let parsed = CommandType::from_str(s).unwrap();
536 assert_eq!(*ct, parsed, "roundtrip failed for {}", s);
537 }
538 }
539
540 #[test]
541 fn test_event_type_roundtrip() {
542 for et in &[
543 EventType::OrderAction,
544 EventType::OrderUpdate,
545 EventType::OrderInfo,
546 EventType::MarketAction,
547 EventType::MarketUpdate,
548 EventType::OrderFilled,
549 EventType::OrderbookUpdated,
550 EventType::L2Update,
551 EventType::Trade,
552 EventType::TransactionRequest,
553 EventType::TransactionUpdate,
554 EventType::MmpTriggered,
555 EventType::PositionExpired,
556 EventType::TierUpdate,
557 EventType::HypercorePositionUpdate,
558 EventType::LiquidationStateChange,
559 EventType::RfqFilled,
560 ] {
561 let s = et.as_str();
562 let parsed = EventType::from_str(s).unwrap();
563 assert_eq!(*et, parsed, "roundtrip failed for {}", s);
564 }
565 }
566
567 #[test]
568 fn test_command_type_from_str_unknown() {
569 assert_eq!(CommandType::from_str("Unknown"), None);
570 }
571
572 #[test]
573 fn test_event_type_from_str_unknown() {
574 assert_eq!(EventType::from_str("Unknown"), None);
575 }
576
577 #[test]
578 fn test_directive_action_key_db_roundtrip() {
579 use hypercall_types::directives::ActionKey;
580
581 let all_variants = [
582 DirectiveActionKeyDb::HlLimitOrder,
583 DirectiveActionKeyDb::HlCancelByOid,
584 DirectiveActionKeyDb::HlCancelByCloid,
585 DirectiveActionKeyDb::HcUpdateApiWallet,
586 DirectiveActionKeyDb::HlSendAsset,
587 DirectiveActionKeyDb::HcTransferOption,
588 DirectiveActionKeyDb::RsmHlLimitOrder,
589 DirectiveActionKeyDb::RsmHlCancelByOid,
590 DirectiveActionKeyDb::RsmHlCancelByCloid,
591 DirectiveActionKeyDb::RsmHlSendAsset,
592 DirectiveActionKeyDb::SystemCreditToken,
593 DirectiveActionKeyDb::SystemCreditOption,
594 DirectiveActionKeyDb::SystemStartLiquidation,
595 DirectiveActionKeyDb::SystemStopLiquidation,
596 DirectiveActionKeyDb::SystemWithdrawToken,
597 ];
598
599 for db_key in all_variants {
600 let s = db_key.as_str();
601 let parsed = DirectiveActionKeyDb::from_str(s).unwrap();
602 assert_eq!(db_key, parsed, "roundtrip failed for {}", s);
603
604 let domain_key: ActionKey = db_key.into();
606 let back: DirectiveActionKeyDb = domain_key.into();
607 assert_eq!(db_key, back, "domain roundtrip failed for {}", s);
608 }
609 }
610
611 #[test]
612 fn test_directive_action_key_db_from_str_unknown() {
613 assert_eq!(DirectiveActionKeyDb::from_str("not_real"), None);
614 }
615}