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,
12    replica_query::ReplicaQueryError,
13};
14use canic_contracts::{
15    dto::wire::projection::overview::RoleStatusResponse, protocol::CANIC_PUBLIC_STATUS,
16};
17use std::path::Path;
18use thiserror::Error as ThisError;
19
20const ICP_JSON_OUTPUT: &str = "json";
21
22///
23/// CanisterReadyQueryError
24///
25
26#[derive(Debug, ThisError)]
27pub enum CanisterReadyQueryError {
28    #[error(transparent)]
29    IcpConfig(#[from] IcpConfigError),
30
31    #[error(transparent)]
32    Icp(#[from] IcpCommandError),
33
34    #[error(transparent)]
35    Replica(#[from] ReplicaQueryError),
36
37    #[error(transparent)]
38    Response(#[from] IcpJsonResponseError),
39}
40
41/// Query role-owned readiness, using the local replica API for local targets.
42pub fn query_canister_ready(
43    icp: &IcpCli,
44    canister_id: &str,
45    environment: &str,
46    icp_root: Option<&Path>,
47    binding: &ResolvedProtocolBinding,
48) -> Result<bool, CanisterReadyQueryError> {
49    if replica_query::uses_local_replica_transport(Some(environment), icp_root)? {
50        return query_local_canister_ready(environment, canister_id, icp_root).map_err(Into::into);
51    }
52
53    query_canister_ready_with_icp(icp, canister_id, binding)
54}
55
56/// Query role-owned readiness directly through the local replica API.
57pub fn query_local_canister_ready(
58    environment: &str,
59    canister_id: &str,
60    icp_root: Option<&Path>,
61) -> Result<bool, ReplicaQueryError> {
62    replica_query::query_ready(
63        Some(environment),
64        canister_id,
65        CANIC_PUBLIC_STATUS,
66        icp_root,
67    )
68}
69
70fn query_canister_ready_with_icp(
71    icp: &IcpCli,
72    canister_id: &str,
73    binding: &ResolvedProtocolBinding,
74) -> Result<bool, CanisterReadyQueryError> {
75    let output = icp.canister_query_arg_output_with_candid(
76        canister_id,
77        CANIC_PUBLIC_STATUS,
78        "(variant { Overview })",
79        Some(ICP_JSON_OUTPUT),
80        Some(&binding.candid_path),
81    )?;
82    let response = decode_json_result_response::<RoleStatusResponse>(&output)?;
83    let RoleStatusResponse::Overview(response) = response;
84    Ok(response.bootstrap.ready)
85}