pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum Supplied<T> {
Absent,
Decoded(T),
Undecoded,
}
impl<T> Supplied<T> {
#[must_use]
pub fn is_absent(&self) -> bool {
matches!(self, Self::Absent)
}
#[must_use]
pub fn is_undecoded(&self) -> bool {
matches!(self, Self::Undecoded)
}
pub fn decoded(&self) -> Option<&T> {
match self {
Self::Decoded(value) => Some(value),
Self::Absent | Self::Undecoded => None,
}
}
}
fn describe_reason(code: Option<&str>, message: Option<&str>) -> String {
match (code, message) {
(Some(code), Some(message)) => format!("{code}: {message}"),
(Some(code), None) => format!("{code} (OANDA sent no error message)"),
(None, Some(message)) => message.to_owned(),
(None, None) => "OANDA sent no readable reason".to_owned(),
}
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum OperationError<R> {
#[error(transparent)]
Client(#[from] Error),
#[error(
"OANDA rejected the operation with HTTP {status}: {}",
describe_reason(.code.as_deref(), .message.as_deref())
)]
Rejected {
status: u16,
code: Option<String>,
message: Option<String>,
body: Supplied<R>,
},
}
#[derive(Debug, Clone, Default, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GenericRejection {
pub error_code: Option<String>,
pub error_message: Option<String>,
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
#[error("OANDA client invariant failed: {0}")]
Invariant(&'static str),
#[error("invalid client input: {0}")]
InvalidInput(String),
#[error("OANDA request rate capacity exhausted")]
RateLimited,
#[error("OANDA returned HTTP 429")]
ProviderRateLimited,
#[error(
"OANDA returned HTTP {status}: {}",
describe_reason(.code.as_deref(), .message.as_deref())
)]
Provider {
status: u16,
code: Option<String>,
message: Option<String>,
},
#[error("OANDA mutation outcome is ambiguous for account {account}")]
AmbiguousMutation {
account: String,
},
#[error("OANDA account {account} requires reconciliation")]
ReconciliationRequired {
account: String,
},
#[error("OANDA account {account} has a mutation in flight")]
MutationInFlight {
account: String,
},
#[error("OANDA transport failed: {0}")]
Transport(String),
#[error("OANDA response exceeds the configured size limit")]
ResponseTooLarge,
#[error("malformed OANDA response: {0}")]
Decode(String),
}