Skip to main content

tea_protocol/
sequence.rs

1use std::fmt;
2use std::str::FromStr;
3
4use serde::{Deserialize, Deserializer, Serialize, Serializer};
5use thiserror::Error;
6
7/// A monotonically increasing, session-local record or event sequence.
8#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
9pub struct SessionSequence(u64);
10
11impl SessionSequence {
12    /// Creates a sequence from its integer representation.
13    #[must_use]
14    pub const fn new(value: u64) -> Self {
15        Self(value)
16    }
17
18    /// Returns the integer representation.
19    #[must_use]
20    pub const fn get(self) -> u64 {
21        self.0
22    }
23
24    /// Returns the next sequence, or `None` on integer overflow.
25    #[must_use]
26    pub const fn checked_next(self) -> Option<Self> {
27        match self.0.checked_add(1) {
28            Some(value) => Some(Self(value)),
29            None => None,
30        }
31    }
32}
33
34/// Error returned when parsing a session sequence.
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
36pub enum SessionSequenceParseError {
37    /// The value is not canonical unsigned decimal text.
38    #[error("session sequence must use canonical unsigned decimal text")]
39    InvalidFormat,
40    /// The value exceeds the supported integer range.
41    #[error("session sequence is out of range")]
42    OutOfRange,
43}
44
45impl fmt::Display for SessionSequence {
46    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
47        self.0.fmt(formatter)
48    }
49}
50
51impl FromStr for SessionSequence {
52    type Err = SessionSequenceParseError;
53
54    fn from_str(value: &str) -> Result<Self, Self::Err> {
55        if value.is_empty()
56            || !value.bytes().all(|byte| byte.is_ascii_digit())
57            || (value.len() > 1 && value.starts_with('0'))
58        {
59            return Err(SessionSequenceParseError::InvalidFormat);
60        }
61        value
62            .parse()
63            .map(Self)
64            .map_err(|_| SessionSequenceParseError::OutOfRange)
65    }
66}
67
68impl Serialize for SessionSequence {
69    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
70    where
71        S: Serializer,
72    {
73        serializer.collect_str(self)
74    }
75}
76
77impl<'de> Deserialize<'de> for SessionSequence {
78    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
79    where
80        D: Deserializer<'de>,
81    {
82        let value = String::deserialize(deserializer)?;
83        value.parse().map_err(serde::de::Error::custom)
84    }
85}