Skip to main content

canic_core/ids/subnet/
mod.rs

1//! Module: ids::subnet
2//!
3//! Responsibility: identify one exact physical IC Subnet.
4//! Does not own: placement selection, trusted-network metadata, or Subnet eligibility.
5//! Boundary: preserves the principal wire shape while preventing Canister/Subnet confusion.
6
7use candid::{CandidType, Principal};
8use serde::{Deserialize, Serialize};
9use std::fmt;
10
11///
12/// SubnetId
13///
14/// Strongly typed physical IC Subnet principal used by protected Fleet bindings.
15///
16
17#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
18#[serde(transparent)]
19pub struct SubnetId(Principal);
20
21impl SubnetId {
22    #[must_use]
23    pub const fn from_principal(principal: Principal) -> Self {
24        Self(principal)
25    }
26
27    #[must_use]
28    pub const fn as_principal(&self) -> &Principal {
29        &self.0
30    }
31
32    #[must_use]
33    pub const fn into_principal(self) -> Principal {
34        self.0
35    }
36}
37
38impl CandidType for SubnetId {
39    fn _ty() -> candid::types::Type {
40        Principal::_ty()
41    }
42
43    fn idl_serialize<S>(&self, serializer: S) -> Result<(), S::Error>
44    where
45        S: candid::types::Serializer,
46    {
47        self.0.idl_serialize(serializer)
48    }
49}
50
51impl fmt::Display for SubnetId {
52    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
53        self.0.fmt(formatter)
54    }
55}
56
57impl From<Principal> for SubnetId {
58    fn from(principal: Principal) -> Self {
59        Self::from_principal(principal)
60    }
61}
62
63impl From<SubnetId> for Principal {
64    fn from(subnet: SubnetId) -> Self {
65        subnet.into_principal()
66    }
67}
68
69// -----------------------------------------------------------------------------
70// Tests
71// -----------------------------------------------------------------------------
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76
77    #[test]
78    fn subnet_id_preserves_the_principal_candid_shape() {
79        let principal = Principal::from_slice(&[7; 29]);
80        let subnet = SubnetId::from_principal(principal);
81        let bytes = candid::encode_one(subnet).expect("encode Subnet ID");
82        let decoded_principal: Principal =
83            candid::decode_one(&bytes).expect("decode Subnet ID as principal");
84        let decoded_subnet: SubnetId =
85            candid::decode_one(&bytes).expect("decode principal as Subnet ID");
86
87        assert_eq!(decoded_principal, principal);
88        assert_eq!(decoded_subnet, subnet);
89    }
90}