Skip to main content

nectar_primitives/chunk/
type_id.rs

1//! Chunk type identification
2//!
3//! This module provides the [`ChunkTypeId`] type for identifying chunk types
4//! at the wire level (serialization/deserialization).
5
6use core::fmt;
7
8/// Wire-level chunk type identifier.
9///
10/// This type represents the type ID byte used in chunk headers for serialization
11/// and deserialization dispatch. It provides type-safe constants for known chunk
12/// types and supports custom types.
13///
14/// # Type ID Ranges
15/// - `0-127`: Reserved for standard Swarm chunk types
16/// - `128-255`: Available for custom/experimental chunk types
17///
18/// # Examples
19///
20/// ```
21/// use nectar_primitives::ChunkTypeId;
22///
23/// // Use predefined constants
24/// let content_type = ChunkTypeId::CONTENT;
25/// let soc_type = ChunkTypeId::SINGLE_OWNER;
26///
27/// // Create custom type ID
28/// let custom_type = ChunkTypeId::custom(200);
29///
30/// // Compare type IDs
31/// assert_ne!(content_type, soc_type);
32/// assert_eq!(content_type.as_u8(), 0);
33/// ```
34#[derive(Clone, Copy, PartialEq, Eq, Hash)]
35pub struct ChunkTypeId(u8);
36
37impl ChunkTypeId {
38    /// Content-addressed chunk type (CAC).
39    ///
40    /// These chunks have their address derived from the BMT hash of their content.
41    pub const CONTENT: Self = Self(0);
42
43    /// Single-owner chunk type (SOC).
44    ///
45    /// These chunks include owner identification and a digital signature.
46    pub const SINGLE_OWNER: Self = Self(1);
47
48    // Reserved type IDs for future standard types:
49    // 2 - Encrypted chunk (planned)
50    // 3 - Manifest chunk (planned)
51    // 4-127 - Reserved for future standard types
52
53    /// Create a new chunk type ID from a raw byte value.
54    ///
55    /// This is a const fn allowing use in const contexts.
56    #[inline]
57    pub const fn new(id: u8) -> Self {
58        Self(id)
59    }
60
61    /// Create a custom chunk type ID.
62    ///
63    /// Custom types should use IDs in the range 128-255 to avoid conflicts
64    /// with standard types.
65    ///
66    /// # Examples
67    ///
68    /// ```
69    /// use nectar_primitives::ChunkTypeId;
70    ///
71    /// let custom = ChunkTypeId::custom(200);
72    /// assert!(custom.is_custom());
73    /// ```
74    #[inline]
75    pub const fn custom(id: u8) -> Self {
76        Self(id)
77    }
78
79    /// Get the raw byte value of this type ID.
80    #[inline]
81    pub const fn as_u8(self) -> u8 {
82        self.0
83    }
84
85    /// Check if this is a standard (reserved) type ID.
86    ///
87    /// Standard types have IDs in the range 0-127.
88    #[inline]
89    pub const fn is_standard(self) -> bool {
90        self.0 < 128
91    }
92
93    /// Check if this is a custom type ID.
94    ///
95    /// Custom types have IDs in the range 128-255.
96    #[inline]
97    pub const fn is_custom(self) -> bool {
98        self.0 >= 128
99    }
100
101    /// Get the human-readable name for known type IDs.
102    ///
103    /// Returns `None` for unknown or custom types.
104    pub const fn name(self) -> Option<&'static str> {
105        match self.0 {
106            0 => Some("content"),
107            1 => Some("single_owner"),
108            _ => None,
109        }
110    }
111
112    /// Get the abbreviated name for known type IDs.
113    ///
114    /// Returns common abbreviations like "CAC" for content-addressed chunks
115    /// and "SOC" for single-owner chunks. Returns `None` for unknown or custom types.
116    ///
117    /// # Examples
118    ///
119    /// ```
120    /// use nectar_primitives::ChunkTypeId;
121    ///
122    /// assert_eq!(ChunkTypeId::CONTENT.abbreviation(), Some("CAC"));
123    /// assert_eq!(ChunkTypeId::SINGLE_OWNER.abbreviation(), Some("SOC"));
124    /// assert_eq!(ChunkTypeId::custom(200).abbreviation(), None);
125    /// ```
126    pub const fn abbreviation(self) -> Option<&'static str> {
127        match self.0 {
128            0 => Some("CAC"),
129            1 => Some("SOC"),
130            _ => None,
131        }
132    }
133}
134
135impl fmt::Debug for ChunkTypeId {
136    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137        match self.name() {
138            Some(name) => write!(f, "ChunkTypeId::{}({})", name.to_uppercase(), self.0),
139            None => write!(f, "ChunkTypeId({})", self.0),
140        }
141    }
142}
143
144impl fmt::Display for ChunkTypeId {
145    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
146        match self.name() {
147            Some(name) => write!(f, "{}", name),
148            None => write!(f, "custom({})", self.0),
149        }
150    }
151}
152
153impl From<u8> for ChunkTypeId {
154    #[inline]
155    fn from(id: u8) -> Self {
156        Self(id)
157    }
158}
159
160impl From<ChunkTypeId> for u8 {
161    #[inline]
162    fn from(id: ChunkTypeId) -> Self {
163        id.0
164    }
165}
166
167#[cfg(test)]
168mod tests {
169    use super::*;
170
171    #[test]
172    fn test_constants() {
173        assert_eq!(ChunkTypeId::CONTENT.as_u8(), 0);
174        assert_eq!(ChunkTypeId::SINGLE_OWNER.as_u8(), 1);
175    }
176
177    #[test]
178    fn test_equality() {
179        assert_eq!(ChunkTypeId::CONTENT, ChunkTypeId::new(0));
180        assert_eq!(ChunkTypeId::SINGLE_OWNER, ChunkTypeId::new(1));
181        assert_ne!(ChunkTypeId::CONTENT, ChunkTypeId::SINGLE_OWNER);
182    }
183
184    #[test]
185    fn test_is_standard() {
186        assert!(ChunkTypeId::CONTENT.is_standard());
187        assert!(ChunkTypeId::SINGLE_OWNER.is_standard());
188        assert!(ChunkTypeId::new(127).is_standard());
189        assert!(!ChunkTypeId::new(128).is_standard());
190        assert!(!ChunkTypeId::custom(200).is_standard());
191    }
192
193    #[test]
194    fn test_is_custom() {
195        assert!(!ChunkTypeId::CONTENT.is_custom());
196        assert!(!ChunkTypeId::SINGLE_OWNER.is_custom());
197        assert!(!ChunkTypeId::new(127).is_custom());
198        assert!(ChunkTypeId::new(128).is_custom());
199        assert!(ChunkTypeId::custom(200).is_custom());
200    }
201
202    #[test]
203    fn test_name() {
204        assert_eq!(ChunkTypeId::CONTENT.name(), Some("content"));
205        assert_eq!(ChunkTypeId::SINGLE_OWNER.name(), Some("single_owner"));
206        assert_eq!(ChunkTypeId::new(50).name(), None);
207        assert_eq!(ChunkTypeId::custom(200).name(), None);
208    }
209
210    #[test]
211    fn test_abbreviation() {
212        assert_eq!(ChunkTypeId::CONTENT.abbreviation(), Some("CAC"));
213        assert_eq!(ChunkTypeId::SINGLE_OWNER.abbreviation(), Some("SOC"));
214        assert_eq!(ChunkTypeId::new(50).abbreviation(), None);
215        assert_eq!(ChunkTypeId::custom(200).abbreviation(), None);
216    }
217
218    #[test]
219    fn test_conversions() {
220        let id: ChunkTypeId = 5u8.into();
221        assert_eq!(id.as_u8(), 5);
222
223        let byte: u8 = ChunkTypeId::CONTENT.into();
224        assert_eq!(byte, 0);
225    }
226
227    #[test]
228    fn test_debug_display() {
229        assert_eq!(
230            format!("{:?}", ChunkTypeId::CONTENT),
231            "ChunkTypeId::CONTENT(0)"
232        );
233        assert_eq!(
234            format!("{:?}", ChunkTypeId::SINGLE_OWNER),
235            "ChunkTypeId::SINGLE_OWNER(1)"
236        );
237        assert_eq!(
238            format!("{:?}", ChunkTypeId::custom(200)),
239            "ChunkTypeId(200)"
240        );
241
242        assert_eq!(format!("{}", ChunkTypeId::CONTENT), "content");
243        assert_eq!(format!("{}", ChunkTypeId::SINGLE_OWNER), "single_owner");
244        assert_eq!(format!("{}", ChunkTypeId::custom(200)), "custom(200)");
245    }
246
247    #[test]
248    fn test_hash() {
249        use std::collections::HashSet;
250
251        let mut set = HashSet::new();
252        set.insert(ChunkTypeId::CONTENT);
253        set.insert(ChunkTypeId::SINGLE_OWNER);
254        set.insert(ChunkTypeId::custom(200));
255
256        assert!(set.contains(&ChunkTypeId::CONTENT));
257        assert!(set.contains(&ChunkTypeId::SINGLE_OWNER));
258        assert!(set.contains(&ChunkTypeId::custom(200)));
259        assert!(!set.contains(&ChunkTypeId::custom(201)));
260    }
261}