use std::fmt;
use std::str::FromStr;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BondRole {
Maker,
Taker,
}
impl fmt::Display for BondRole {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
BondRole::Maker => f.write_str("maker"),
BondRole::Taker => f.write_str("taker"),
}
}
}
impl FromStr for BondRole {
type Err = BondParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"maker" => Ok(BondRole::Maker),
"taker" => Ok(BondRole::Taker),
other => Err(BondParseError::UnknownRole(other.to_string())),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BondState {
Requested,
Locked,
Released,
PendingPayout,
Slashed,
Forfeited,
Failed,
}
impl BondState {
pub fn is_terminal(self) -> bool {
matches!(
self,
BondState::Released | BondState::Slashed | BondState::Forfeited | BondState::Failed
)
}
pub fn is_active(self) -> bool {
matches!(self, BondState::Requested | BondState::Locked)
}
}
impl fmt::Display for BondState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
BondState::Requested => "requested",
BondState::Locked => "locked",
BondState::Released => "released",
BondState::PendingPayout => "pending-payout",
BondState::Slashed => "slashed",
BondState::Forfeited => "forfeited",
BondState::Failed => "failed",
};
f.write_str(s)
}
}
impl FromStr for BondState {
type Err = BondParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"requested" => Ok(BondState::Requested),
"locked" => Ok(BondState::Locked),
"released" => Ok(BondState::Released),
"pending-payout" => Ok(BondState::PendingPayout),
"slashed" => Ok(BondState::Slashed),
"forfeited" => Ok(BondState::Forfeited),
"failed" => Ok(BondState::Failed),
other => Err(BondParseError::UnknownState(other.to_string())),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BondSlashReason {
LostDispute,
Timeout,
}
impl fmt::Display for BondSlashReason {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
BondSlashReason::LostDispute => f.write_str("lost-dispute"),
BondSlashReason::Timeout => f.write_str("timeout"),
}
}
}
impl FromStr for BondSlashReason {
type Err = BondParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"lost-dispute" => Ok(BondSlashReason::LostDispute),
"timeout" => Ok(BondSlashReason::Timeout),
other => Err(BondParseError::UnknownSlashReason(other.to_string())),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BondParseError {
UnknownRole(String),
UnknownState(String),
UnknownSlashReason(String),
}
impl fmt::Display for BondParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
BondParseError::UnknownRole(v) => write!(f, "unknown bond role: {v}"),
BondParseError::UnknownState(v) => write!(f, "unknown bond state: {v}"),
BondParseError::UnknownSlashReason(v) => {
write!(f, "unknown bond slash reason: {v}")
}
}
}
}
impl std::error::Error for BondParseError {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn role_roundtrip() {
for r in [BondRole::Maker, BondRole::Taker] {
assert_eq!(BondRole::from_str(&r.to_string()).unwrap(), r);
}
}
#[test]
fn state_roundtrip() {
for s in [
BondState::Requested,
BondState::Locked,
BondState::Released,
BondState::PendingPayout,
BondState::Slashed,
BondState::Forfeited,
BondState::Failed,
] {
assert_eq!(BondState::from_str(&s.to_string()).unwrap(), s);
}
}
#[test]
fn slash_reason_roundtrip() {
for s in [BondSlashReason::LostDispute, BondSlashReason::Timeout] {
assert_eq!(BondSlashReason::from_str(&s.to_string()).unwrap(), s);
}
}
#[test]
fn unknown_parse_rejected() {
assert!(BondRole::from_str("solver").is_err());
assert!(BondState::from_str("in-progress").is_err());
assert!(BondSlashReason::from_str("whatever").is_err());
}
#[test]
fn terminal_and_active_helpers() {
for s in [
BondState::Released,
BondState::Slashed,
BondState::Forfeited,
BondState::Failed,
] {
assert!(s.is_terminal(), "{s} should be terminal");
assert!(!s.is_active(), "{s} should not be active");
}
for s in [BondState::Requested, BondState::Locked] {
assert!(s.is_active(), "{s} should be active");
assert!(!s.is_terminal(), "{s} should not be terminal");
}
assert!(!BondState::PendingPayout.is_terminal());
assert!(!BondState::PendingPayout.is_active());
}
}