use candid_parser::candid::{CandidType, de::DecoderConfig, utils::decode_one_with_config};
use serde::de::DeserializeOwned;
use thiserror::Error;
#[cfg(test)]
mod tests;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct DecodeLimits {
pub reply_bytes: usize,
pub decoding_quota: usize,
pub skipping_quota: usize,
pub type_entries: usize,
pub header_bytes: usize,
}
#[derive(Debug, Error)]
pub enum DecodeError {
#[error("Candid reply exceeds its byte limit")]
ReplyBytes,
#[error("Candid reply exceeds the remaining aggregate byte budget")]
AggregateBytes,
#[error("Candid reply could not be decoded: {0}")]
Candid(#[source] candid_parser::candid::Error),
}
pub fn decode_one<T: CandidType + DeserializeOwned>(
bytes: &[u8],
limits: DecodeLimits,
remaining: &mut usize,
) -> Result<T, DecodeError> {
if bytes.len() > limits.reply_bytes {
return Err(DecodeError::ReplyBytes);
}
*remaining = remaining
.checked_sub(bytes.len())
.ok_or(DecodeError::AggregateBytes)?;
let mut config = DecoderConfig::new();
config
.set_decoding_quota(limits.decoding_quota)
.set_skipping_quota(limits.skipping_quota)
.set_max_type_len(limits.type_entries)
.set_max_header_len(limits.header_bytes);
decode_one_with_config(bytes, &config).map_err(DecodeError::Candid)
}