Skip to main content

cesr/core/matter/code/
number.rs

1use super::cesr_code::CesrCode;
2use super::matter_code::MatterCode;
3use super::sealed::Sealed;
4use crate::core::matter::error::ValidationError;
5#[cfg(feature = "alloc")]
6#[allow(
7    unused_imports,
8    reason = "alloc prelude items; subset used per cfg/feature combination"
9)]
10use alloc::string::ToString;
11
12/// CESR codes for unsigned integer primitives, ordered from smallest to largest.
13#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
14pub enum NumberCode {
15    /// 2-byte unsigned integer (`M`).
16    Short,
17    /// 4-byte unsigned integer (`0H`).
18    Long,
19    /// 5-byte unsigned integer (`R`).
20    Tall,
21    /// 8-byte unsigned integer (`N`).
22    Big,
23    /// 11-byte unsigned integer (`S`).
24    Large,
25    /// 14-byte unsigned integer (`T`).
26    Great,
27    /// 17-byte unsigned integer (`U`).
28    Vast,
29}
30
31impl NumberCode {
32    /// Maximum unsigned integer value this code can represent.
33    ///
34    /// # Errors
35    ///
36    /// Returns a `ValidationError` for variable-size codes whose raw size
37    /// cannot be determined.
38    pub fn max_value(&self) -> Result<u128, ValidationError> {
39        let raw_size = self.to_matter_code().raw_size()?;
40        if raw_size >= 16 {
41            Ok(u128::MAX)
42        } else {
43            Ok((1u128 << (raw_size * 8)) - 1)
44        }
45    }
46
47    /// Pick the smallest `NumberCode` that can hold the given value.
48    #[must_use]
49    pub const fn for_value(value: u128) -> Self {
50        match value {
51            0..=0xFFFF => Self::Short,
52            0x1_0000..=0xFFFF_FFFF => Self::Long,
53            0x1_0000_0000..=0xFF_FFFF_FFFF => Self::Tall,
54            0x100_0000_0000..=0xFFFF_FFFF_FFFF_FFFF => Self::Big,
55            _ => Self::Vast,
56        }
57    }
58}
59
60impl Sealed for NumberCode {}
61
62impl CesrCode for NumberCode {
63    fn to_matter_code(&self) -> MatterCode {
64        match self {
65            Self::Short => MatterCode::Short,
66            Self::Long => MatterCode::Long,
67            Self::Tall => MatterCode::Tall,
68            Self::Big => MatterCode::Big,
69            Self::Large => MatterCode::Large,
70            Self::Great => MatterCode::Great,
71            Self::Vast => MatterCode::Vast,
72        }
73    }
74
75    fn as_str(&self) -> &'static str {
76        match self {
77            Self::Short => "M",
78            Self::Long => "0H",
79            Self::Tall => "R",
80            Self::Big => "N",
81            Self::Large => "S",
82            Self::Great => "T",
83            Self::Vast => "U",
84        }
85    }
86}
87
88impl TryFrom<MatterCode> for NumberCode {
89    type Error = ValidationError;
90
91    fn try_from(code: MatterCode) -> Result<Self, Self::Error> {
92        match code {
93            MatterCode::Short => Ok(Self::Short),
94            MatterCode::Long => Ok(Self::Long),
95            MatterCode::Tall => Ok(Self::Tall),
96            MatterCode::Big => Ok(Self::Big),
97            MatterCode::Large => Ok(Self::Large),
98            MatterCode::Great => Ok(Self::Great),
99            MatterCode::Vast => Ok(Self::Vast),
100            _ => Err(ValidationError::UnknownMatterCode(code.to_string())),
101        }
102    }
103}
104
105impl From<NumberCode> for MatterCode {
106    fn from(code: NumberCode) -> Self {
107        code.to_matter_code()
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use super::*;
114    use crate::core::matter::code::MatterCode;
115
116    #[test]
117    fn number_code_to_matter_code_roundtrip() {
118        let codes = [
119            (NumberCode::Short, MatterCode::Short),
120            (NumberCode::Long, MatterCode::Long),
121            (NumberCode::Tall, MatterCode::Tall),
122            (NumberCode::Big, MatterCode::Big),
123            (NumberCode::Large, MatterCode::Large),
124            (NumberCode::Great, MatterCode::Great),
125            (NumberCode::Vast, MatterCode::Vast),
126        ];
127        for (nc, mc) in codes {
128            assert_eq!(nc.to_matter_code(), mc);
129            assert_eq!(NumberCode::try_from(mc).unwrap(), nc);
130            assert_eq!(MatterCode::from(nc), mc);
131        }
132    }
133
134    #[test]
135    fn number_code_rejects_non_number() {
136        assert!(NumberCode::try_from(MatterCode::Ed25519).is_err());
137        assert!(NumberCode::try_from(MatterCode::Blake3_256).is_err());
138        assert!(NumberCode::try_from(MatterCode::Ed25519Seed).is_err());
139    }
140
141    #[test]
142    fn number_code_as_str() {
143        assert_eq!(NumberCode::Short.as_str(), "M");
144        assert_eq!(NumberCode::Long.as_str(), "0H");
145        assert_eq!(NumberCode::Big.as_str(), "N");
146        assert_eq!(NumberCode::Vast.as_str(), "U");
147    }
148
149    #[test]
150    fn number_code_max_value() {
151        assert_eq!(NumberCode::Short.max_value().unwrap(), 0xFFFF);
152        assert_eq!(NumberCode::Long.max_value().unwrap(), 0xFFFF_FFFF);
153        assert_eq!(NumberCode::Big.max_value().unwrap(), 0xFFFF_FFFF_FFFF_FFFF);
154    }
155
156    #[test]
157    fn number_code_for_value() {
158        assert_eq!(NumberCode::for_value(0), NumberCode::Short);
159        assert_eq!(NumberCode::for_value(255), NumberCode::Short);
160        assert_eq!(NumberCode::for_value(0xFFFF), NumberCode::Short);
161        assert_eq!(NumberCode::for_value(0x1_0000), NumberCode::Long);
162        assert_eq!(NumberCode::for_value(0xFFFF_FFFF), NumberCode::Long);
163        assert_eq!(NumberCode::for_value(0x1_0000_0000), NumberCode::Tall);
164        assert_eq!(NumberCode::for_value(0x1_0000_0000_0000), NumberCode::Big);
165        assert_eq!(NumberCode::for_value(u128::from(u64::MAX)), NumberCode::Big);
166        assert_eq!(NumberCode::for_value(u128::MAX), NumberCode::Vast);
167    }
168}