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