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::utils::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
22#[derive(
23    CandidType, Clone, Debug, Eq, Ord, Display, PartialOrd, Deserialize, Serialize, PartialEq, Hash,
24)]
25#[serde(transparent)]
26pub struct CanisterRole(pub Cow<'static, str>);
27
28impl CanisterRole {
29    pub const ROOT: Self = Self(Cow::Borrowed("root"));
30
31    #[must_use]
32    pub const fn new(s: &'static str) -> Self {
33        Self(Cow::Borrowed(s))
34    }
35
36    #[must_use]
37    pub const fn owned(s: String) -> Self {
38        Self(Cow::Owned(s))
39    }
40
41    #[must_use]
42    pub fn as_str(&self) -> &str {
43        &self.0
44    }
45
46    /// Returns true if this type represents the built-in ROOT canister.
47    #[must_use]
48    pub fn is_root(&self) -> bool {
49        self.0.as_ref() == "root"
50    }
51
52    /// Convert into an owned string (avoids an extra allocation for owned variants).
53    #[must_use]
54    pub fn into_string(self) -> String {
55        self.0.into_owned()
56    }
57}
58
59impl FromStr for CanisterRole {
60    type Err = String;
61
62    fn from_str(s: &str) -> Result<Self, Self::Err> {
63        Ok(Self::owned(s.to_string()))
64    }
65}
66
67impl From<&'static str> for CanisterRole {
68    fn from(s: &'static str) -> Self {
69        Self(Cow::Borrowed(s))
70    }
71}
72
73impl From<&String> for CanisterRole {
74    fn from(s: &String) -> Self {
75        Self(Cow::Owned(s.clone()))
76    }
77}
78
79impl From<String> for CanisterRole {
80    fn from(s: String) -> Self {
81        Self(Cow::Owned(s))
82    }
83}
84
85impl From<CanisterRole> for String {
86    fn from(ct: CanisterRole) -> Self {
87        ct.into_string()
88    }
89}
90
91impl AsRef<str> for CanisterRole {
92    fn as_ref(&self) -> &str {
93        self.as_str()
94    }
95}
96
97impl Borrow<str> for CanisterRole {
98    fn borrow(&self) -> &str {
99        self.as_str()
100    }
101}
102
103impl_storable_bounded!(CanisterRole, 64, false);
104
105///
106/// TESTS
107///
108
109#[cfg(test)]
110mod tests {
111    use super::CanisterRole;
112    #[test]
113    fn basic_traits_and_utils() {
114        let a = CanisterRole::ROOT;
115        assert!(a.is_root());
116        assert_eq!(a.as_str(), "root");
117        let b: CanisterRole = "example".into();
118        assert_eq!(b.as_str(), "example");
119        let s: String = b.clone().into();
120        assert_eq!(s, "example");
121        assert_eq!(b.as_ref(), "example");
122    }
123}