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/// Owned by ids and shared across config, storage, DTOs, and workflows.
23///
24
25const PRIME_ROLE: &str = "prime";
26
27#[derive(
28    CandidType, Clone, Debug, Eq, Ord, PartialOrd, Deserialize, Serialize, PartialEq, Hash,
29)]
30#[serde(transparent)]
31pub struct SubnetRole(pub Cow<'static, str>);
32
33impl SubnetRole {
34    pub const PRIME: Self = Self(Cow::Borrowed(PRIME_ROLE));
35
36    /// Create a borrowed static subnet role.
37    #[must_use]
38    pub const fn new(s: &'static str) -> Self {
39        Self(Cow::Borrowed(s))
40    }
41
42    /// Create an owned subnet role.
43    #[must_use]
44    pub const fn owned(s: String) -> Self {
45        Self(Cow::Owned(s))
46    }
47
48    /// Return the subnet role as text.
49    #[must_use]
50    pub fn as_str(&self) -> &str {
51        &self.0
52    }
53
54    /// Return whether this role is the built-in prime subnet role.
55    #[must_use]
56    pub fn is_prime(&self) -> bool {
57        self.0.as_ref() == PRIME_ROLE
58    }
59
60    /// Convert into an owned string (avoids an extra allocation for owned variants).
61    #[must_use]
62    pub fn into_string(self) -> String {
63        self.0.into_owned()
64    }
65}
66
67impl FromStr for SubnetRole {
68    type Err = String;
69
70    fn from_str(s: &str) -> Result<Self, Self::Err> {
71        Ok(Self::owned(s.to_string()))
72    }
73}
74
75impl From<&'static str> for SubnetRole {
76    fn from(s: &'static str) -> Self {
77        Self(Cow::Borrowed(s))
78    }
79}
80
81impl From<&String> for SubnetRole {
82    fn from(s: &String) -> Self {
83        Self(Cow::Owned(s.clone()))
84    }
85}
86
87impl From<String> for SubnetRole {
88    fn from(s: String) -> Self {
89        Self(Cow::Owned(s))
90    }
91}
92
93impl From<SubnetRole> for String {
94    fn from(ct: SubnetRole) -> Self {
95        ct.into_string()
96    }
97}
98
99impl AsRef<str> for SubnetRole {
100    fn as_ref(&self) -> &str {
101        self.as_str()
102    }
103}
104
105impl Borrow<str> for SubnetRole {
106    fn borrow(&self) -> &str {
107        self.as_str()
108    }
109}
110
111impl fmt::Display for SubnetRole {
112    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113        f.write_str(self.as_str())
114    }
115}
116
117impl_storable_bounded!(SubnetRole, 64, false);
118
119// -----------------------------------------------------------------------------
120// Tests
121// -----------------------------------------------------------------------------
122
123#[cfg(test)]
124mod tests {
125    use super::*;
126
127    #[test]
128    fn basic_traits_and_utils() {
129        let a = SubnetRole::PRIME;
130        assert!(a.is_prime());
131        assert_eq!(a.as_str(), "prime");
132        let b: SubnetRole = "example".into();
133        assert_eq!(b.as_str(), "example");
134        let s: String = b.clone().into();
135        assert_eq!(s, "example");
136        assert_eq!(b.as_ref(), "example");
137    }
138}