use serde::{Deserialize, Serialize};
use thiserror::Error as ThisError;
#[derive(Debug, ThisError)]
#[error("{0}")]
pub(super) struct CborError(String);
pub(super) enum QueryOutcome {
Replied(Vec<u8>),
Rejected { code: u64, message: String },
}
pub(super) fn encode_anonymous_query(
canister_id: &[u8],
method_name: &str,
arg: &[u8],
ingress_expiry: u64,
) -> Result<Vec<u8>, CborError> {
let sender = candid::Principal::anonymous();
let envelope = QueryEnvelope {
content: QueryContent {
request_type: "query",
canister_id,
method_name,
arg,
sender: sender.as_slice(),
ingress_expiry,
},
};
let mut bytes = Vec::new();
ciborium::ser::into_writer(&envelope, &mut bytes).map_err(cbor_error)?;
Ok(bytes)
}
pub(super) fn decode_query_response(bytes: &[u8]) -> Result<QueryOutcome, CborError> {
let response = ciborium::de::from_reader::<QueryResponse, _>(bytes).map_err(cbor_error)?;
match response.status {
QueryResponseStatus::Replied => response
.reply
.map(|reply| QueryOutcome::Replied(reply.arg))
.ok_or_else(|| CborError("missing query reply".to_string())),
QueryResponseStatus::Rejected => Ok(QueryOutcome::Rejected {
code: response.reject_code.unwrap_or_default(),
message: response.reject_message.unwrap_or_default(),
}),
}
}
fn cbor_error(error: impl std::fmt::Display) -> CborError {
CborError(error.to_string())
}
#[derive(Serialize)]
struct QueryEnvelope<'a> {
content: QueryContent<'a>,
}
#[derive(Serialize)]
struct QueryContent<'a> {
request_type: &'static str,
#[serde(with = "serde_bytes")]
canister_id: &'a [u8],
method_name: &'a str,
#[serde(with = "serde_bytes")]
arg: &'a [u8],
#[serde(with = "serde_bytes")]
sender: &'a [u8],
ingress_expiry: u64,
}
#[derive(Deserialize)]
struct QueryResponse {
status: QueryResponseStatus,
reply: Option<QueryReply>,
reject_code: Option<u64>,
reject_message: Option<String>,
}
#[derive(Deserialize)]
#[serde(rename_all = "lowercase")]
enum QueryResponseStatus {
Rejected,
Replied,
}
#[derive(Deserialize)]
struct QueryReply {
#[serde(with = "serde_bytes")]
arg: Vec<u8>,
}
#[cfg(test)]
mod tests;