pub const SEQ_NO_MAX_VALUE: u32 = 0xFFFFFFFF;
pub const SEQ_NO_SUBMAX_VALUE: u32 = 0xFFFFFFFE;
pub trait SeqNoExt {
#[inline]
fn classify(self) -> SeqNoClass;
#[inline]
fn is_rbf(self) -> bool;
}
impl SeqNoExt for SeqNo {
#[inline]
fn classify(self) -> SeqNoClass {
match self.0 {
SEQ_NO_MAX_VALUE | SEQ_NO_SUBMAX_VALUE => SeqNoClass::Unencumbered,
no if no & SEQ_NO_CSV_DISABLE_MASK != 0 => SeqNoClass::RbfOnly,
no if no & SEQ_NO_CSV_TYPE_MASK != 0 => SeqNoClass::RelativeTime,
_ => SeqNoClass::RelativeHeight,
}
}
#[inline]
fn is_rbf(self) -> bool { self.0 < SEQ_NO_SUBMAX_VALUE }
}
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
pub enum SeqNoClass {
Unencumbered,
RbfOnly,
RelativeTime,
RelativeHeight,
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub struct Rbf(SeqNo);
impl Rbf {
#[inline]
pub fn unencumbered(max: bool) -> SeqNo {
SeqNo(if max { SEQ_NO_MAX_VALUE } else { SEQ_NO_SUBMAX_VALUE })
}
#[inline]
pub fn from_rbf(order: u16) -> SeqNo { SeqNo(order as u32 | SEQ_NO_CSV_DISABLE_MASK) }
#[inline]
pub fn rbf() -> SeqNo { SeqNo(SEQ_NO_SUBMAX_VALUE - 1) }
}
#[derive(Debug, Clone, PartialEq, Eq, From, Display)]
#[display(doc_comments)]
pub enum ParseError {
#[from]
InvalidNumber(ParseIntError),
InvalidHeight(u32),
InvalidTimestamp(u32),
InvalidDescriptor(String),
NoRand,
}
impl std::error::Error for ParseError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
ParseError::InvalidNumber(err) => Some(err),
_ => None,
}
}
}
impl Display for Rbf {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self.classify() {
SeqNoClass::Unencumbered if self.0 == SEQ_NO_MAX_VALUE => {
f.write_str("final(0xFFFFFFFF)")
}
SeqNoClass::Unencumbered if self.0 == SEQ_NO_SUBMAX_VALUE => {
f.write_str("non-rbf(0xFFFFFFFE)")
}
SeqNoClass::Unencumbered => unreachable!(),
SeqNoClass::RbfOnly => {
f.write_str("rbf(")?;
Display::fmt(&(self.0 ^ SEQ_NO_CSV_DISABLE_MASK), f)?;
f.write_str(")")
}
_ if self.0 >> 16 & 0xFFBF > 0 => Display::fmt(&self.0, f),
SeqNoClass::RelativeTime => {
let value = self.0 & 0xFFFF;
f.write_str("time(")?;
Display::fmt(&value, f)?;
f.write_str(")")
}
SeqNoClass::RelativeHeight => {
let value = self.0 & 0xFFFF;
f.write_str("height(")?;
Display::fmt(&value, f)?;
f.write_str(")")
}
}
}
}
impl FromStr for Rbf {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.to_lowercase();
if s == "rbf" {
#[cfg(feature = "rand")]
{
Ok(SeqNo::rbf())
}
#[cfg(not(feature = "rand"))]
{
Err(ParseError::NoRand)
}
} else if s.starts_with("rbf(") && s.ends_with(')') {
let no = s[4..].trim_end_matches(')').parse()?;
Ok(SeqNo::from_rbf(no))
} else if s.starts_with("time(") && s.ends_with(')') {
let no = s[5..].trim_end_matches(')').parse()?;
Ok(SeqNo::from_intervals(no))
} else if s.starts_with("height(") && s.ends_with(')') {
let no = s[7..].trim_end_matches(')').parse()?;
Ok(SeqNo::from_height(no))
} else {
let no = s.parse()?;
Ok(SeqNo(no))
}
}
}