Skip to main content

canic_core/api/
role.rs

1//! Module: api::role
2//!
3//! Responsibility: adapt the compiled role contract into immutable status verification.
4//! Does not own: capability derivation, endpoint emission, authorization, or mutable state.
5//! Boundary: maps internal capability keys to the closed public role overview values.
6
7use crate::{
8    dto::{
9        metadata::CanicMetadataResponse,
10        role::{RoleCapability, RoleOverviewResponse},
11        state::BootstrapStatusResponse,
12    },
13    ids::CanisterRole,
14    role_contract::RoleCapabilityKey,
15};
16use std::collections::BTreeSet;
17
18/// Endpoint-facing adapter for immutable compiled-role verification.
19pub struct RoleOverviewApi;
20
21impl RoleOverviewApi {
22    /// Build one overview from the already resolved build-time capability authority.
23    #[must_use]
24    pub fn overview(
25        role: CanisterRole,
26        capabilities: &BTreeSet<RoleCapabilityKey>,
27        protocol_profile_digest: [u8; 32],
28        metadata: CanicMetadataResponse,
29        bootstrap: BootstrapStatusResponse,
30    ) -> RoleOverviewResponse {
31        let mut capabilities = capabilities
32            .iter()
33            .copied()
34            .filter_map(capability_view)
35            .collect::<Vec<_>>();
36        capabilities.sort_unstable();
37
38        RoleOverviewResponse {
39            role,
40            capabilities,
41            protocol_profile_digest,
42            metadata,
43            bootstrap,
44        }
45    }
46}
47
48const fn capability_view(capability: RoleCapabilityKey) -> Option<RoleCapability> {
49    Some(match capability {
50        RoleCapabilityKey::AutomaticTopup => RoleCapability::AutomaticTopup,
51        RoleCapabilityKey::ChildProvisioning => RoleCapability::ChildProvisioning,
52        RoleCapabilityKey::DelegatedTokenIssuer => RoleCapability::DelegatedTokenIssuer,
53        RoleCapabilityKey::DelegatedTokenVerifier => RoleCapability::DelegatedTokenVerifier,
54        RoleCapabilityKey::FleetAdmissionProjection => RoleCapability::FleetAdmissionProjection,
55        RoleCapabilityKey::FleetCoordinator => RoleCapability::FleetCoordinator,
56        RoleCapabilityKey::Index => RoleCapability::Index,
57        RoleCapabilityKey::Icrc21 => RoleCapability::Icrc21,
58        RoleCapabilityKey::LocalApplicationAuthorization => return None,
59        RoleCapabilityKey::RoleAttestationSigner => RoleCapability::RoleAttestationSigner,
60        RoleCapabilityKey::RoleAttestationVerifier => RoleCapability::RoleAttestationVerifier,
61        RoleCapabilityKey::Root => RoleCapability::Root,
62        RoleCapabilityKey::RootControlPlane => RoleCapability::RootControlPlane,
63        RoleCapabilityKey::RootDelegation => RoleCapability::RootDelegation,
64        RoleCapabilityKey::Runtime => RoleCapability::Runtime,
65        RoleCapabilityKey::Scaling => RoleCapability::Scaling,
66        RoleCapabilityKey::Sharding => RoleCapability::Sharding,
67        RoleCapabilityKey::WasmStore => RoleCapability::WasmStore,
68    })
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74
75    #[test]
76    fn overview_projects_public_capabilities_in_lexical_order() {
77        let capabilities = BTreeSet::from([
78            RoleCapabilityKey::AutomaticTopup,
79            RoleCapabilityKey::ChildProvisioning,
80            RoleCapabilityKey::DelegatedTokenIssuer,
81            RoleCapabilityKey::DelegatedTokenVerifier,
82            RoleCapabilityKey::FleetAdmissionProjection,
83            RoleCapabilityKey::FleetCoordinator,
84            RoleCapabilityKey::Index,
85            RoleCapabilityKey::Icrc21,
86            RoleCapabilityKey::LocalApplicationAuthorization,
87            RoleCapabilityKey::RoleAttestationSigner,
88            RoleCapabilityKey::RoleAttestationVerifier,
89            RoleCapabilityKey::Root,
90            RoleCapabilityKey::RootControlPlane,
91            RoleCapabilityKey::Runtime,
92            RoleCapabilityKey::Scaling,
93            RoleCapabilityKey::Sharding,
94            RoleCapabilityKey::WasmStore,
95        ]);
96        let overview = RoleOverviewApi::overview(
97            CanisterRole::new("app"),
98            &capabilities,
99            [9; 32],
100            CanicMetadataResponse {
101                package_name: "app".to_string(),
102                package_version: "0.1.0".to_string(),
103                package_description: "test".to_string(),
104                canic_version: "0.103.0".to_string(),
105                canister_version: 4,
106            },
107            BootstrapStatusResponse {
108                ready: true,
109                phase: "ready".to_string(),
110                last_error: None,
111            },
112        );
113
114        assert_eq!(
115            overview.capabilities,
116            vec![
117                RoleCapability::AutomaticTopup,
118                RoleCapability::ChildProvisioning,
119                RoleCapability::DelegatedTokenIssuer,
120                RoleCapability::DelegatedTokenVerifier,
121                RoleCapability::FleetAdmissionProjection,
122                RoleCapability::FleetCoordinator,
123                RoleCapability::Icrc21,
124                RoleCapability::Index,
125                RoleCapability::RoleAttestationSigner,
126                RoleCapability::RoleAttestationVerifier,
127                RoleCapability::Root,
128                RoleCapability::RootControlPlane,
129                RoleCapability::Runtime,
130                RoleCapability::Scaling,
131                RoleCapability::Sharding,
132                RoleCapability::WasmStore,
133            ]
134        );
135        assert_eq!(overview.protocol_profile_digest, [9; 32]);
136    }
137}