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::Runtime => RoleCapability::Runtime,
64        RoleCapabilityKey::Scaling => RoleCapability::Scaling,
65        RoleCapabilityKey::Sharding => RoleCapability::Sharding,
66        RoleCapabilityKey::WasmStore => RoleCapability::WasmStore,
67    })
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73
74    #[test]
75    fn overview_projects_public_capabilities_in_lexical_order() {
76        let capabilities = BTreeSet::from([
77            RoleCapabilityKey::AutomaticTopup,
78            RoleCapabilityKey::ChildProvisioning,
79            RoleCapabilityKey::DelegatedTokenIssuer,
80            RoleCapabilityKey::DelegatedTokenVerifier,
81            RoleCapabilityKey::FleetAdmissionProjection,
82            RoleCapabilityKey::FleetCoordinator,
83            RoleCapabilityKey::Index,
84            RoleCapabilityKey::Icrc21,
85            RoleCapabilityKey::LocalApplicationAuthorization,
86            RoleCapabilityKey::RoleAttestationSigner,
87            RoleCapabilityKey::RoleAttestationVerifier,
88            RoleCapabilityKey::Root,
89            RoleCapabilityKey::RootControlPlane,
90            RoleCapabilityKey::Runtime,
91            RoleCapabilityKey::Scaling,
92            RoleCapabilityKey::Sharding,
93            RoleCapabilityKey::WasmStore,
94        ]);
95        let overview = RoleOverviewApi::overview(
96            CanisterRole::new("app"),
97            &capabilities,
98            [9; 32],
99            CanicMetadataResponse {
100                package_name: "app".to_string(),
101                package_version: "0.1.0".to_string(),
102                package_description: "test".to_string(),
103                canic_version: "0.103.0".to_string(),
104                canister_version: 4,
105            },
106            BootstrapStatusResponse {
107                ready: true,
108                phase: "ready".to_string(),
109                last_error: None,
110            },
111        );
112
113        assert_eq!(
114            overview.capabilities,
115            vec![
116                RoleCapability::AutomaticTopup,
117                RoleCapability::ChildProvisioning,
118                RoleCapability::DelegatedTokenIssuer,
119                RoleCapability::DelegatedTokenVerifier,
120                RoleCapability::FleetAdmissionProjection,
121                RoleCapability::FleetCoordinator,
122                RoleCapability::Icrc21,
123                RoleCapability::Index,
124                RoleCapability::RoleAttestationSigner,
125                RoleCapability::RoleAttestationVerifier,
126                RoleCapability::Root,
127                RoleCapability::RootControlPlane,
128                RoleCapability::Runtime,
129                RoleCapability::Scaling,
130                RoleCapability::Sharding,
131                RoleCapability::WasmStore,
132            ]
133        );
134        assert_eq!(overview.protocol_profile_digest, [9; 32]);
135    }
136}