#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum ErrorCodeTable {
Login,
Logout,
Validate,
SuccessOnly,
ContractSearchById,
Bars,
OrderSearch,
OrderSearchById,
OrderPlacement,
OrderCancellation,
OrderModification,
PositionSearch,
PositionClose,
PartialPositionClose,
TradeSearch,
}
impl ErrorCodeTable {
pub(crate) fn name(self, code: i32) -> Option<&'static str> {
usize::try_from(code)
.ok()
.and_then(|index| self.published_names().get(index))
.copied()
}
const fn published_names(self) -> &'static [&'static str] {
match self {
Self::Login => &[
"Success",
"UserNotFound",
"PasswordVerificationFailed",
"InvalidCredentials",
"AppNotFound",
"AppVerificationFailed",
"InvalidDevice",
"AgreementsNotSigned",
"UnknownError",
"ApiSubscriptionNotFound",
"ApiKeyAuthenticationDisabled",
],
Self::Logout => &["Success", "InvalidSession", "UnknownError"],
Self::Validate => &[
"Success",
"InvalidSession",
"SessionNotFound",
"ExpiredToken",
"UnknownError",
],
Self::SuccessOnly => &["Success"],
Self::ContractSearchById => &["Success", "ContractNotFound"],
Self::Bars => &[
"Success",
"ContractNotFound",
"UnitInvalid",
"UnitNumberInvalid",
"LimitInvalid",
],
Self::OrderSearch | Self::PositionSearch | Self::TradeSearch => {
&["Success", "AccountNotFound"]
}
Self::OrderSearchById => &["Success", "OrderNotFound"],
Self::OrderPlacement => &[
"Success",
"AccountNotFound",
"OrderRejected",
"InsufficientFunds",
"AccountViolation",
"OutsideTradingHours",
"OrderPending",
"UnknownError",
"ContractNotFound",
"ContractNotActive",
"AccountRejected",
],
Self::OrderCancellation => &[
"Success",
"AccountNotFound",
"OrderNotFound",
"Rejected",
"Pending",
"UnknownError",
"AccountRejected",
],
Self::OrderModification => &[
"Success",
"AccountNotFound",
"OrderNotFound",
"Rejected",
"Pending",
"UnknownError",
"AccountRejected",
"ContractNotFound",
],
Self::PositionClose => &[
"Success",
"AccountNotFound",
"PositionNotFound",
"ContractNotFound",
"ContractNotActive",
"OrderRejected",
"OrderPending",
"UnknownError",
"AccountRejected",
],
Self::PartialPositionClose => &[
"Success",
"AccountNotFound",
"PositionNotFound",
"ContractNotFound",
"ContractNotActive",
"InvalidCloseSize",
"OrderRejected",
"OrderPending",
"UnknownError",
"AccountRejected",
],
}
}
}
#[cfg(test)]
mod tests {
use super::ErrorCodeTable;
#[test]
fn published_names_are_indexed_by_error_code() {
let cases: &[(ErrorCodeTable, i32, &str)] = &[
(ErrorCodeTable::Login, 0, "Success"),
(ErrorCodeTable::Login, 1, "UserNotFound"),
(ErrorCodeTable::Login, 10, "ApiKeyAuthenticationDisabled"),
(ErrorCodeTable::Logout, 1, "InvalidSession"),
(ErrorCodeTable::Validate, 3, "ExpiredToken"),
(ErrorCodeTable::SuccessOnly, 0, "Success"),
(ErrorCodeTable::ContractSearchById, 1, "ContractNotFound"),
(ErrorCodeTable::Bars, 3, "UnitNumberInvalid"),
(ErrorCodeTable::OrderSearch, 1, "AccountNotFound"),
(ErrorCodeTable::OrderSearchById, 1, "OrderNotFound"),
(ErrorCodeTable::OrderPlacement, 2, "OrderRejected"),
(ErrorCodeTable::OrderPlacement, 6, "OrderPending"),
(ErrorCodeTable::OrderPlacement, 10, "AccountRejected"),
(ErrorCodeTable::OrderCancellation, 2, "OrderNotFound"),
(ErrorCodeTable::OrderCancellation, 3, "Rejected"),
(ErrorCodeTable::OrderModification, 7, "ContractNotFound"),
(ErrorCodeTable::PositionSearch, 1, "AccountNotFound"),
(ErrorCodeTable::PositionClose, 5, "OrderRejected"),
(ErrorCodeTable::PartialPositionClose, 5, "InvalidCloseSize"),
(ErrorCodeTable::PartialPositionClose, 9, "AccountRejected"),
(ErrorCodeTable::TradeSearch, 1, "AccountNotFound"),
];
for &(table, code, expected) in cases {
assert_eq!(
table.name(code),
Some(expected),
"{table:?} code {code} must be named {expected}"
);
}
}
#[test]
fn undocumented_and_impossible_codes_have_no_name() {
let documented: &[(ErrorCodeTable, i32)] = &[
(ErrorCodeTable::Login, 10),
(ErrorCodeTable::Logout, 2),
(ErrorCodeTable::Validate, 4),
(ErrorCodeTable::SuccessOnly, 0),
(ErrorCodeTable::ContractSearchById, 1),
(ErrorCodeTable::Bars, 4),
(ErrorCodeTable::OrderSearch, 1),
(ErrorCodeTable::OrderSearchById, 1),
(ErrorCodeTable::OrderPlacement, 10),
(ErrorCodeTable::OrderCancellation, 6),
(ErrorCodeTable::OrderModification, 7),
(ErrorCodeTable::PositionSearch, 1),
(ErrorCodeTable::PositionClose, 8),
(ErrorCodeTable::PartialPositionClose, 9),
(ErrorCodeTable::TradeSearch, 1),
];
for &(table, last_documented) in documented {
let undocumented = last_documented.saturating_add(1);
assert_eq!(
table.name(undocumented),
None,
"{table:?} code {undocumented} must stay unnamed"
);
assert_eq!(table.name(-1), None, "{table:?} codes are never negative");
}
}
}