#[cfg(not(feature = "std"))]
use crate::nostd_prelude::*;
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
#[non_exhaustive]
pub struct StreamId {
pub ms: u64,
pub seq: u64,
}
impl StreamId {
pub const MIN: StreamId = StreamId::new(0, 0);
pub const MAX: StreamId = StreamId::new(u64::MAX, u64::MAX);
pub const fn new(ms: u64, seq: u64) -> Self {
Self { ms, seq }
}
pub fn encode(self) -> Vec<u8> {
format!("{}-{}", self.ms, self.seq).into_bytes()
}
#[must_use]
pub fn next(self) -> Self {
if self.seq < u64::MAX {
StreamId::new(self.ms, self.seq + 1)
} else if self.ms < u64::MAX {
StreamId::new(self.ms + 1, 0)
} else {
StreamId::MAX
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
#[non_exhaustive]
pub enum XAddIdSpec {
AutoAll,
AutoSeq(u64),
Explicit(StreamId),
}
pub fn parse_xadd_id(s: &[u8]) -> Result<XAddIdSpec, StreamIdError> {
if s == b"*" {
return Ok(XAddIdSpec::AutoAll);
}
match s.iter().position(|&b| b == b'-') {
None => Ok(XAddIdSpec::Explicit(StreamId::new(id_part(s)?, 0))),
Some(dash) => {
let ms = id_part(&s[..dash])?;
let seq_s = &s[dash + 1..];
if seq_s == b"*" {
return Ok(XAddIdSpec::AutoSeq(ms));
}
Ok(XAddIdSpec::Explicit(StreamId::new(ms, id_part(seq_s)?)))
}
}
}
fn id_part(s: &[u8]) -> Result<u64, StreamIdError> {
let start = s.iter().position(|b| !b" \t\n\x0b\x0c\r".contains(b)).unwrap_or(s.len());
let digits = s[start..].strip_prefix(b"+").unwrap_or(&s[start..]);
if digits.is_empty() || !digits.iter().all(u8::is_ascii_digit) {
return Err(StreamIdError::Invalid);
}
digits
.iter()
.try_fold(0u64, |n, d| n.checked_mul(10)?.checked_add(u64::from(d - b'0')))
.ok_or(StreamIdError::Invalid)
}
pub fn parse_range_start(s: &[u8]) -> Result<StreamId, StreamIdError> {
match s {
b"-" => Ok(StreamId::MIN),
b"+" => Ok(StreamId::MAX),
_ => parse_explicit_id(s),
}
}
pub fn parse_range_end(s: &[u8]) -> Result<StreamId, StreamIdError> {
match s {
b"+" => Ok(StreamId::MAX),
b"-" => Ok(StreamId::MIN),
_ => parse_id(s, u64::MAX),
}
}
pub fn parse_explicit_id(s: &[u8]) -> Result<StreamId, StreamIdError> {
parse_id(s, 0)
}
fn parse_id(s: &[u8], bare_seq: u64) -> Result<StreamId, StreamIdError> {
match s.iter().position(|&b| b == b'-') {
Some(dash) => Ok(StreamId::new(id_part(&s[..dash])?, id_part(&s[dash + 1..])?)),
None => Ok(StreamId::new(id_part(s)?, bare_seq)),
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum StreamIdError {
Invalid,
}
impl core::fmt::Display for StreamIdError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Invalid => f.write_str("invalid stream id"),
}
}
}
impl core::error::Error for StreamIdError {}