canic_core/ids/
canister.rs

1//!
2//! Strongly-typed identifiers representing canister roles within the project.
3//! Provides string-backed wrappers with storage traits and helpers for config
4//! parsing while avoiding repeated `Cow` boilerplate around the codebase.
5//!
6
7use crate::memory::impl_storable_bounded;
8use candid::CandidType;
9use derive_more::Display;
10use serde::{Deserialize, Serialize};
11use std::{borrow::Borrow, borrow::Cow, str::FromStr};
12
13///
14/// CanisterRole
15///
16/// A human-readable identifier for a canister role/type (e.g., "root", "example").
17///
18/// Stored as `Cow<'static, str>` so known constants can be zero‑copy while
19/// dynamic values allocate only when needed.
20///
21
22const ROOT_ROLE: &str = "root";
23
24#[derive(
25    CandidType, Clone, Debug, Eq, Ord, Display, PartialOrd, Deserialize, Serialize, PartialEq, Hash,
26)]
27#[serde(transparent)]
28pub struct CanisterRole(pub Cow<'static, str>);
29
30impl CanisterRole {
31    pub const ROOT: Self = Self(Cow::Borrowed(ROOT_ROLE));
32
33    #[must_use]
34    pub const fn new(s: &'static str) -> Self {
35        Self(Cow::Borrowed(s))
36    }
37
38    #[must_use]
39    pub const fn owned(s: String) -> Self {
40        Self(Cow::Owned(s))
41    }
42
43    #[must_use]
44    pub fn as_str(&self) -> &str {
45        &self.0
46    }
47
48    /// Returns true if this type represents the built-in ROOT canister.
49    #[must_use]
50    pub fn is_root(&self) -> bool {
51        self.0.as_ref() == ROOT_ROLE
52    }
53
54    /// Convert into an owned string (avoids an extra allocation for owned variants).
55    #[must_use]
56    pub fn into_string(self) -> String {
57        self.0.into_owned()
58    }
59}
60
61impl FromStr for CanisterRole {
62    type Err = String;
63
64    fn from_str(s: &str) -> Result<Self, Self::Err> {
65        Ok(Self::owned(s.to_string()))
66    }
67}
68
69impl From<&'static str> for CanisterRole {
70    fn from(s: &'static str) -> Self {
71        Self(Cow::Borrowed(s))
72    }
73}
74
75impl From<&String> for CanisterRole {
76    fn from(s: &String) -> Self {
77        Self(Cow::Owned(s.clone()))
78    }
79}
80
81impl From<String> for CanisterRole {
82    fn from(s: String) -> Self {
83        Self(Cow::Owned(s))
84    }
85}
86
87impl From<CanisterRole> for String {
88    fn from(ct: CanisterRole) -> Self {
89        ct.into_string()
90    }
91}
92
93impl AsRef<str> for CanisterRole {
94    fn as_ref(&self) -> &str {
95        self.as_str()
96    }
97}
98
99impl Borrow<str> for CanisterRole {
100    fn borrow(&self) -> &str {
101        self.as_str()
102    }
103}
104
105impl_storable_bounded!(CanisterRole, 64, false);
106
107///
108/// TESTS
109///
110
111#[cfg(test)]
112mod tests {
113    use super::CanisterRole;
114    #[test]
115    fn basic_traits_and_utils() {
116        let a = CanisterRole::ROOT;
117        assert!(a.is_root());
118        assert_eq!(a.as_str(), "root");
119        let b: CanisterRole = "example".into();
120        assert_eq!(b.as_str(), "example");
121        let s: String = b.clone().into();
122        assert_eq!(s, "example");
123        assert_eq!(b.as_ref(), "example");
124    }
125}