canic_core/api/ic/canic/
endpoint.rs1use crate::{dto::error::Error, ids::CanisterRole};
2
3#[derive(Clone, Debug)]
15pub struct ProtectedInternalEndpoint {
16 method: &'static str,
17 accepted_roles: Vec<CanisterRole>,
18}
19
20impl ProtectedInternalEndpoint {
21 #[must_use]
22 #[track_caller]
23 pub fn new(method: &'static str, roles: impl IntoIterator<Item = CanisterRole>) -> Self {
24 assert!(
25 !method.trim().is_empty(),
26 "protected internal endpoint descriptor method must not be empty"
27 );
28 let accepted_roles = roles.into_iter().collect::<Vec<_>>();
29 assert!(
30 !accepted_roles.is_empty(),
31 "protected internal endpoint descriptor '{method}' must accept at least one caller role"
32 );
33 for (index, role) in accepted_roles.iter().enumerate() {
34 assert!(
35 !role.as_str().trim().is_empty(),
36 "protected internal endpoint descriptor '{method}' has an empty caller role at index {index}"
37 );
38 assert!(
39 !accepted_roles[..index].iter().any(|prior| prior == role),
40 "protected internal endpoint descriptor '{method}' contains duplicate caller role '{role}'"
41 );
42 }
43 Self {
44 method,
45 accepted_roles,
46 }
47 }
48
49 #[must_use]
50 pub const fn method(&self) -> &'static str {
51 self.method
52 }
53
54 #[must_use]
55 pub fn accepted_roles(&self) -> &[CanisterRole] {
56 &self.accepted_roles
57 }
58
59 #[must_use]
60 pub fn accepted_roles_label(&self) -> String {
61 self.accepted_roles
62 .iter()
63 .map(ToString::to_string)
64 .collect::<Vec<_>>()
65 .join(", ")
66 }
67
68 #[must_use]
69 pub fn accepts_role(&self, role: &CanisterRole) -> bool {
70 self.accepted_roles.iter().any(|accepted| accepted == role)
71 }
72
73 #[must_use]
74 pub fn single_role(&self) -> Option<&CanisterRole> {
75 match self.accepted_roles.as_slice() {
76 [role] => Some(role),
77 _ => None,
78 }
79 }
80
81 pub fn required_single_role(&self) -> Result<CanisterRole, Error> {
82 self.single_role().cloned().ok_or_else(|| {
83 Error::invalid(format!(
84 "protected internal endpoint '{}' accepts {} roles [{}]; choose a caller role explicitly",
85 self.method(),
86 self.accepted_roles.len(),
87 self.accepted_roles_label()
88 ))
89 })
90 }
91}