use super::{DecodeError, DecodeLimits, nesting::NestingError};
use candid::utils::ArgumentDecoder;
use std::{error::Error, fmt};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum EmptyArgs {
Reject,
Unit,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ArgumentLimits {
pub max_bytes: usize,
pub decoding_quota: usize,
pub skipping_quota: usize,
pub max_type_len: usize,
pub max_header_len: usize,
}
#[derive(Debug)]
pub enum ArgumentDecodeError {
TooLarge {
actual: usize,
maximum: usize,
},
Candid(candid::Error),
}
impl fmt::Display for ArgumentDecodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::TooLarge { actual, maximum } => write!(
f,
"argument envelope is {actual} bytes; maximum is {maximum}"
),
Self::Candid(_) => f.write_str("invalid or over-budget Candid arguments"),
}
}
}
impl Error for ArgumentDecodeError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::TooLarge { .. } => None,
Self::Candid(error) => Some(error),
}
}
}
impl ArgumentDecodeError {
pub fn nesting_error(&self) -> Option<&NestingError> {
match self {
Self::Candid(candid::Error::Custom(error)) => error.downcast_ref(),
_ => None,
}
}
}
impl ArgumentLimits {
pub fn admit_len(self, actual: usize) -> Result<(), ArgumentDecodeError> {
if actual > self.max_bytes {
return Err(ArgumentDecodeError::TooLarge {
actual,
maximum: self.max_bytes,
});
}
Ok(())
}
pub fn decode<'a, T: ArgumentDecoder<'a>>(
self,
bytes: &'a [u8],
empty: EmptyArgs,
) -> Result<T, ArgumentDecodeError> {
self.admit_len(bytes.len())?;
let bytes = if bytes.is_empty() && empty == EmptyArgs::Unit {
&b"DIDL\0\0"[..]
} else {
bytes
};
super::decode_args(
bytes,
DecodeLimits {
reply_bytes: self.max_bytes.max(bytes.len()),
decoding_quota: self.decoding_quota,
skipping_quota: self.skipping_quota,
type_entries: self.max_type_len,
header_bytes: self.max_header_len,
},
)
.map_err(|error| match error {
DecodeError::ReplyTooLarge { actual, maximum } => {
ArgumentDecodeError::TooLarge { actual, maximum }
}
DecodeError::Candid(error) => ArgumentDecodeError::Candid(error),
})
}
}