canic_host/replica_query/
mod.rs1mod cbor;
8mod status;
9#[cfg(test)]
10mod tests;
11mod transport;
12
13use self::transport::local_query;
14use std::path::Path;
15
16use candid::{CandidType, Decode, Deserialize, Encode};
17use canic_core::{
18 dto::{error::Error as CanicError, role::RoleOverviewResponse},
19 ids::BuildNetwork,
20};
21use thiserror::Error as ThisError;
22
23use crate::icp_config::{
24 IcpConfigError, resolve_current_canic_icp_root, resolve_icp_build_network_from_root,
25};
26
27pub use self::status::local_replica_status_reachable_from_root;
28
29#[derive(CandidType)]
30enum RoleStatusRequest {
31 Overview,
32}
33
34#[derive(CandidType, Deserialize)]
35enum RoleStatusResponse {
36 Overview(RoleOverviewResponse),
37}
38
39#[derive(Debug, ThisError)]
44pub enum ReplicaQueryError {
45 #[error(transparent)]
46 Candid(candid::Error),
47
48 #[error("{0}")]
49 Canister(CanicError),
50
51 #[error("{0}")]
52 Cbor(String),
53
54 #[error(transparent)]
55 Io(#[from] std::io::Error),
56
57 #[error("{0}")]
58 Query(String),
59
60 #[error("local replica rejected query: code={code} message={message}")]
61 Rejected { code: u64, message: String },
62}
63
64impl From<cbor::CborError> for ReplicaQueryError {
65 fn from(err: cbor::CborError) -> Self {
67 Self::Cbor(err.to_string())
68 }
69}
70
71pub fn uses_local_replica_transport(
73 environment: Option<&str>,
74 icp_root: Option<&Path>,
75) -> Result<bool, IcpConfigError> {
76 let Some(environment) = environment else {
77 return Ok(true);
78 };
79 if environment.starts_with("http://") {
80 return Ok(true);
81 }
82
83 let discovered_root;
84 let root = if let Some(root) = icp_root {
85 root
86 } else {
87 discovered_root = resolve_current_canic_icp_root()?;
88 &discovered_root
89 };
90 Ok(resolve_icp_build_network_from_root(root, environment)? == BuildNetwork::Local)
91}
92
93pub(crate) fn query_ready(
95 environment: Option<&str>,
96 canister: &str,
97 endpoint: &str,
98 icp_root: Option<&Path>,
99) -> Result<bool, ReplicaQueryError> {
100 let arg = Encode!(&RoleStatusRequest::Overview).map_err(ReplicaQueryError::Candid)?;
101 let bytes = local_query(environment, canister, endpoint, &arg, icp_root)?;
102 let result = Decode!(&bytes, Result<RoleStatusResponse, CanicError>)
103 .map_err(ReplicaQueryError::Candid)?;
104 let RoleStatusResponse::Overview(response) = result.map_err(ReplicaQueryError::Canister)?;
105 Ok(response.bootstrap.ready)
106}