1use candid::{
10 CandidType,
11 de::{DecoderConfig, IDLDeserialize},
12};
13use serde::Deserialize;
14use std::{error::Error, fmt};
15
16#[cfg(test)]
17mod tests;
18
19#[derive(Clone, Copy, Debug, Eq, PartialEq)]
21pub struct DecodeLimits {
22 pub reply_bytes: usize,
24 pub decoding_quota: usize,
26 pub skipping_quota: usize,
28 pub type_entries: usize,
30 pub header_bytes: usize,
32}
33
34#[derive(Debug)]
36pub enum DecodeError {
37 ReplyTooLarge {
39 actual: usize,
41 maximum: usize,
43 },
44 Candid(candid::Error),
46}
47
48impl fmt::Display for DecodeError {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 match self {
51 Self::ReplyTooLarge { actual, maximum } => {
52 write!(f, "Candid reply is {actual} bytes, maximum is {maximum}")
53 }
54 Self::Candid(error) => write!(f, "Candid reply decoding failed: {error}"),
55 }
56 }
57}
58
59impl Error for DecodeError {
60 fn source(&self) -> Option<&(dyn Error + 'static)> {
61 match self {
62 Self::ReplyTooLarge { .. } => None,
63 Self::Candid(error) => Some(error),
64 }
65 }
66}
67
68fn config(bytes: &[u8], limits: DecodeLimits) -> Result<DecoderConfig, DecodeError> {
69 if bytes.len() > limits.reply_bytes {
70 return Err(DecodeError::ReplyTooLarge {
71 actual: bytes.len(),
72 maximum: limits.reply_bytes,
73 });
74 }
75 let mut config = DecoderConfig::new();
76 config
77 .set_decoding_quota(limits.decoding_quota)
78 .set_skipping_quota(limits.skipping_quota)
79 .set_max_type_len(limits.type_entries)
80 .set_max_header_len(limits.header_bytes)
81 .set_full_error_message(false);
82 Ok(config)
83}
84
85pub fn decode_one<'a, T: Deserialize<'a> + CandidType>(
90 bytes: &'a [u8],
91 limits: DecodeLimits,
92) -> Result<T, DecodeError> {
93 candid::decode_one_with_config(bytes, &config(bytes, limits)?).map_err(DecodeError::Candid)
94}
95
96pub fn decode_args<'a, T: candid::utils::ArgumentDecoder<'a>>(
104 bytes: &'a [u8],
105 limits: DecodeLimits,
106) -> Result<T, DecodeError> {
107 candid::decode_args_with_config(bytes, &config(bytes, limits)?).map_err(DecodeError::Candid)
108}
109
110pub fn decode_exactly_one<'a, T: Deserialize<'a> + CandidType>(
115 bytes: &'a [u8],
116 limits: DecodeLimits,
117) -> Result<T, DecodeError> {
118 let config = config(bytes, limits)?;
119 let mut decoder =
120 IDLDeserialize::new_with_config(bytes, &config).map_err(DecodeError::Candid)?;
121 if decoder.is_done() {
122 return Err(DecodeError::Candid(candid::Error::msg(
123 "expected exactly one Candid result",
124 )));
125 }
126 let value = decoder.get_value::<T>().map_err(DecodeError::Candid)?;
127 if !decoder.is_done() {
128 return Err(DecodeError::Candid(candid::Error::msg(
129 "expected exactly one Candid result",
130 )));
131 }
132 decoder.done().map_err(DecodeError::Candid)?;
133 Ok(value)
134}