use alloy_primitives::U64;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::{fmt, str::FromStr};
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
pub enum BlockNumber {
#[default]
Latest,
Finalized,
Safe,
Earliest,
Pending,
Number(U64),
}
impl BlockNumber {
pub fn as_number(&self) -> Option<U64> {
match *self {
BlockNumber::Number(num) => Some(num),
_ => None,
}
}
pub fn is_number(&self) -> bool {
matches!(self, BlockNumber::Number(_))
}
pub fn is_latest(&self) -> bool {
matches!(self, BlockNumber::Latest)
}
pub fn is_finalized(&self) -> bool {
matches!(self, BlockNumber::Finalized)
}
pub fn is_safe(&self) -> bool {
matches!(self, BlockNumber::Safe)
}
pub fn is_pending(&self) -> bool {
matches!(self, BlockNumber::Pending)
}
pub fn is_earliest(&self) -> bool {
matches!(self, BlockNumber::Earliest)
}
}
impl<T: Into<U64>> From<T> for BlockNumber {
fn from(num: T) -> Self {
BlockNumber::Number(num.into())
}
}
impl Serialize for BlockNumber {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match *self {
BlockNumber::Number(ref x) => serializer.serialize_str(&format!("0x{x:x}")),
BlockNumber::Latest => serializer.serialize_str("latest"),
BlockNumber::Finalized => serializer.serialize_str("finalized"),
BlockNumber::Safe => serializer.serialize_str("safe"),
BlockNumber::Earliest => serializer.serialize_str("earliest"),
BlockNumber::Pending => serializer.serialize_str("pending"),
}
}
}
impl<'de> Deserialize<'de> for BlockNumber {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?.to_lowercase();
s.parse().map_err(serde::de::Error::custom)
}
}
impl FromStr for BlockNumber {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"latest" => Ok(Self::Latest),
"finalized" => Ok(Self::Finalized),
"safe" => Ok(Self::Safe),
"earliest" => Ok(Self::Earliest),
"pending" => Ok(Self::Pending),
n if n.starts_with("0x") => n.parse().map(Self::Number).map_err(|e| e.to_string()),
n => n.parse::<u64>().map(|n| Self::Number(U64::from(n))).map_err(|e| e.to_string()),
}
}
}
impl fmt::Display for BlockNumber {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
BlockNumber::Number(x) => format!("0x{x:x}").fmt(f),
BlockNumber::Latest => f.write_str("latest"),
BlockNumber::Finalized => f.write_str("finalized"),
BlockNumber::Safe => f.write_str("safe"),
BlockNumber::Earliest => f.write_str("earliest"),
BlockNumber::Pending => f.write_str("pending"),
}
}
}