Skip to main content

canic_core/ids/
subnet.rs

1//! Module: ids::subnet
2//!
3//! Responsibility: subnet role identifiers shared across Canic layers.
4//! Does not own: placement policy, subnet registry state, or authorization.
5//! Boundary: provides stable, bounded subnet role names for storage and DTOs.
6
7use crate::{cdk::candid::CandidType, impl_storable_bounded};
8use serde::{Deserialize, Serialize};
9use std::{
10    borrow::{Borrow, Cow},
11    fmt,
12    str::FromStr,
13};
14
15///
16/// SubnetRole
17///
18/// A human-readable identifier for a subnet type.
19///
20/// Stored as `Cow<'static, str>` so known constants can be zero-copy while
21/// dynamic values allocate only when needed.
22///
23
24const PRIME_ROLE: &str = "prime";
25
26#[derive(
27    CandidType, Clone, Debug, Eq, Ord, PartialOrd, Deserialize, Serialize, PartialEq, Hash,
28)]
29#[serde(transparent)]
30pub struct SubnetRole(pub Cow<'static, str>);
31
32impl SubnetRole {
33    pub const PRIME: Self = Self(Cow::Borrowed(PRIME_ROLE));
34
35    #[must_use]
36    pub const fn new(s: &'static str) -> Self {
37        Self(Cow::Borrowed(s))
38    }
39
40    #[must_use]
41    pub const fn owned(s: String) -> Self {
42        Self(Cow::Owned(s))
43    }
44
45    #[must_use]
46    pub fn as_str(&self) -> &str {
47        &self.0
48    }
49
50    /// Returns true if this type represents the built-in PRIME subnet
51    #[must_use]
52    pub fn is_prime(&self) -> bool {
53        self.0.as_ref() == PRIME_ROLE
54    }
55
56    /// Convert into an owned string (avoids an extra allocation for owned variants).
57    #[must_use]
58    pub fn into_string(self) -> String {
59        self.0.into_owned()
60    }
61}
62
63impl FromStr for SubnetRole {
64    type Err = String;
65
66    fn from_str(s: &str) -> Result<Self, Self::Err> {
67        Ok(Self::owned(s.to_string()))
68    }
69}
70
71impl From<&'static str> for SubnetRole {
72    fn from(s: &'static str) -> Self {
73        Self(Cow::Borrowed(s))
74    }
75}
76
77impl From<&String> for SubnetRole {
78    fn from(s: &String) -> Self {
79        Self(Cow::Owned(s.clone()))
80    }
81}
82
83impl From<String> for SubnetRole {
84    fn from(s: String) -> Self {
85        Self(Cow::Owned(s))
86    }
87}
88
89impl From<SubnetRole> for String {
90    fn from(ct: SubnetRole) -> Self {
91        ct.into_string()
92    }
93}
94
95impl AsRef<str> for SubnetRole {
96    fn as_ref(&self) -> &str {
97        self.as_str()
98    }
99}
100
101impl Borrow<str> for SubnetRole {
102    fn borrow(&self) -> &str {
103        self.as_str()
104    }
105}
106
107impl fmt::Display for SubnetRole {
108    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109        f.write_str(self.as_str())
110    }
111}
112
113impl_storable_bounded!(SubnetRole, 64, false);
114
115///
116/// TESTS
117///
118
119#[cfg(test)]
120mod tests {
121    use super::*;
122
123    #[test]
124    fn basic_traits_and_utils() {
125        let a = SubnetRole::PRIME;
126        assert!(a.is_prime());
127        assert_eq!(a.as_str(), "prime");
128        let b: SubnetRole = "example".into();
129        assert_eq!(b.as_str(), "example");
130        let s: String = b.clone().into();
131        assert_eq!(s, "example");
132        assert_eq!(b.as_ref(), "example");
133    }
134}