Skip to main content

canic_host/canister_ready/
mod.rs

1//! Module: canister_ready
2//!
3//! Responsibility: query the maintained Canic readiness endpoint.
4//! Does not own: readiness state, local replica transport, or install orchestration.
5//! Boundary: selects one transport and decodes the canonical boolean response.
6
7use crate::{
8    icp::{IcpCli, IcpCommandError, IcpJsonResponseError, decode_json_result_response},
9    icp_config::IcpConfigError,
10    protocol_binding::ResolvedProtocolBinding,
11    replica_query::{self, ReplicaQueryError},
12};
13use candid::{CandidType, Deserialize};
14use canic_core::{dto::role::RoleOverviewResponse, protocol::CANIC_PUBLIC_STATUS};
15use std::path::Path;
16use thiserror::Error as ThisError;
17
18const ICP_JSON_OUTPUT: &str = "json";
19
20#[derive(CandidType, Deserialize)]
21enum RoleStatusResponse {
22    Overview(RoleOverviewResponse),
23}
24
25///
26/// CanisterReadyQueryError
27///
28
29#[derive(Debug, ThisError)]
30pub enum CanisterReadyQueryError {
31    #[error(transparent)]
32    IcpConfig(#[from] IcpConfigError),
33
34    #[error(transparent)]
35    Icp(#[from] IcpCommandError),
36
37    #[error(transparent)]
38    Replica(#[from] ReplicaQueryError),
39
40    #[error(transparent)]
41    Response(#[from] IcpJsonResponseError),
42}
43
44/// Query role-owned readiness, using the local replica API for local targets.
45pub fn query_canister_ready(
46    icp: &IcpCli,
47    canister_id: &str,
48    environment: &str,
49    icp_root: Option<&Path>,
50    binding: &ResolvedProtocolBinding,
51) -> Result<bool, CanisterReadyQueryError> {
52    if replica_query::uses_local_replica_transport(Some(environment), icp_root)? {
53        return query_local_canister_ready(environment, canister_id, icp_root).map_err(Into::into);
54    }
55
56    query_canister_ready_with_icp(icp, canister_id, binding)
57}
58
59/// Query role-owned readiness directly through the local replica API.
60pub fn query_local_canister_ready(
61    environment: &str,
62    canister_id: &str,
63    icp_root: Option<&Path>,
64) -> Result<bool, ReplicaQueryError> {
65    replica_query::query_ready(
66        Some(environment),
67        canister_id,
68        CANIC_PUBLIC_STATUS,
69        icp_root,
70    )
71}
72
73fn query_canister_ready_with_icp(
74    icp: &IcpCli,
75    canister_id: &str,
76    binding: &ResolvedProtocolBinding,
77) -> Result<bool, CanisterReadyQueryError> {
78    let output = icp.canister_query_arg_output_with_candid(
79        canister_id,
80        CANIC_PUBLIC_STATUS,
81        "(variant { Overview })",
82        Some(ICP_JSON_OUTPUT),
83        Some(&binding.candid_path),
84    )?;
85    let response = decode_json_result_response::<RoleStatusResponse>(&output)?;
86    let RoleStatusResponse::Overview(response) = response;
87    Ok(response.bootstrap.ready)
88}