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