Skip to main content

canic_host/cycle_balance/
mod.rs

1//! Module: cycle_balance
2//!
3//! Responsibility: query the maintained Canic cycle-balance endpoint.
4//! Does not own: cycle accounting, local replica transport, or report aggregation.
5//! Boundary: decodes the canonical typed endpoint result into a host balance.
6
7#[cfg(test)]
8mod tests;
9
10use crate::{
11    icp::{IcpCli, IcpCommandError, IcpJsonResponseError, decode_json_result_response},
12    icp_config::IcpConfigError,
13    protocol_binding::ResolvedProtocolBinding,
14    replica_query::{self, ReplicaQueryError},
15};
16use candid::{CandidType, Deserialize};
17use canic_core::dto::role::CycleBalanceStatusResponse;
18use std::path::Path;
19use thiserror::Error as ThisError;
20
21use canic_core::protocol;
22
23const ICP_JSON_OUTPUT: &str = "json";
24
25#[derive(CandidType, Deserialize)]
26enum RoleStatusResponse {
27    CycleBalance(CycleBalanceStatusResponse),
28}
29
30///
31/// CycleBalanceQueryError
32///
33
34#[derive(Debug, ThisError)]
35pub enum CycleBalanceQueryError {
36    #[error(transparent)]
37    IcpConfig(#[from] IcpConfigError),
38
39    #[error(transparent)]
40    Icp(#[from] IcpCommandError),
41
42    #[error(transparent)]
43    Replica(#[from] ReplicaQueryError),
44
45    #[error(transparent)]
46    Response(#[from] IcpJsonResponseError),
47}
48
49/// Query the role-owned cycle-balance status through the selected transport.
50pub fn query_cycle_balance(
51    icp: &IcpCli,
52    canister_id: &str,
53    environment: &str,
54    icp_root: Option<&Path>,
55    binding: &ResolvedProtocolBinding,
56) -> Result<u128, CycleBalanceQueryError> {
57    if replica_query::uses_local_replica_transport(Some(environment), icp_root)? {
58        return replica_query::query_cycle_balance(Some(environment), canister_id, icp_root)
59            .map_err(Into::into);
60    }
61
62    let output = icp.canister_query_arg_output_with_candid(
63        canister_id,
64        protocol::CANIC_STATUS,
65        "(variant { CycleBalance })",
66        Some(ICP_JSON_OUTPUT),
67        Some(&binding.candid_path),
68    )?;
69    let response = decode_json_result_response::<RoleStatusResponse>(&output)?;
70    let RoleStatusResponse::CycleBalance(response) = response;
71    Ok(response.cycles)
72}