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