Skip to main content

canic_host/cycle_balance/
mod.rs

1use crate::{
2    icp::{IcpCli, IcpCommandError},
3    replica_query,
4    response_parse::parse_cycle_balance_response,
5};
6use canic_core::protocol;
7use std::{error::Error, fmt, path::Path};
8
9const ICP_JSON_OUTPUT: &str = "json";
10
11///
12/// CycleBalanceQueryError
13///
14
15#[derive(Debug)]
16pub enum CycleBalanceQueryError {
17    Icp(IcpCommandError),
18    Parse { canister: String, output: String },
19}
20
21impl fmt::Display for CycleBalanceQueryError {
22    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
23        match self {
24            Self::Icp(err) => write!(formatter, "{err}"),
25            Self::Parse { canister, output } => write!(
26                formatter,
27                "could not parse {canister} {} response: {output}",
28                protocol::CANIC_CYCLE_BALANCE
29            ),
30        }
31    }
32}
33
34impl Error for CycleBalanceQueryError {
35    fn source(&self) -> Option<&(dyn Error + 'static)> {
36        match self {
37            Self::Icp(err) => Some(err),
38            Self::Parse { .. } => None,
39        }
40    }
41}
42
43impl From<IcpCommandError> for CycleBalanceQueryError {
44    fn from(err: IcpCommandError) -> Self {
45        Self::Icp(err)
46    }
47}
48
49/// Query `canic_cycle_balance`, using direct local replica calls when available.
50pub fn query_cycle_balance(
51    icp: &IcpCli,
52    canister_id: &str,
53    network: &str,
54    icp_root: Option<&Path>,
55    candid_path: Option<&Path>,
56) -> Result<u128, CycleBalanceQueryError> {
57    if replica_query::should_use_local_replica_query(Some(network))
58        && let Some(root) = icp_root
59        && let Ok(cycles) =
60            replica_query::query_cycle_balance_from_root(Some(network), canister_id, root)
61    {
62        return Ok(cycles);
63    }
64
65    let output = icp.canister_query_output_with_candid(
66        canister_id,
67        protocol::CANIC_CYCLE_BALANCE,
68        Some(ICP_JSON_OUTPUT),
69        candid_path,
70    )?;
71    parse_cycle_balance_response(&output).ok_or_else(|| CycleBalanceQueryError::Parse {
72        canister: canister_id.to_string(),
73        output,
74    })
75}
76
77/// Query `canic_cycle_balance` for reporting paths that treat missing live data as absent.
78#[must_use]
79pub fn query_cycle_balance_optional(
80    icp: &IcpCli,
81    canister_id: &str,
82    network: &str,
83    icp_root: Option<&Path>,
84    candid_path: Option<&Path>,
85) -> Option<u128> {
86    query_cycle_balance(icp, canister_id, network, icp_root, candid_path).ok()
87}