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