mod encode;
mod semantic;
mod syntax;
use std::{error::Error, fmt};
pub use encode::{EncodeError, EncodeErrorKind, encode};
pub use semantic::{SemanticError, SemanticErrorKind, decode_tree};
pub use syntax::{DEFAULT_DEPTH_LIMIT, Limits, SyntaxError, parse};
use crate::{DecodeRole, SlotValue, TypeDescriptor};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DecodeError {
Syntax(SyntaxError),
Semantic(SemanticError),
}
impl fmt::Display for DecodeError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Syntax(error) => error.fmt(formatter),
Self::Semantic(error) => error.fmt(formatter),
}
}
}
impl Error for DecodeError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::Syntax(error) => Some(error),
Self::Semantic(error) => Some(error),
}
}
}
pub fn decode(
input: &[u8],
descriptor: &TypeDescriptor,
role: DecodeRole,
limits: Limits,
) -> Result<SlotValue, DecodeError> {
let tree = parse(input, limits).map_err(DecodeError::Syntax)?;
decode_tree(tree, descriptor, role).map_err(DecodeError::Semantic)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{ContractValue as V, OpaquePayload, OpaqueTree, VariantDescriptor, VariantPayload};
fn limits(bytes: &[u8]) -> Limits {
Limits::new(bytes.len(), DEFAULT_DEPTH_LIMIT)
}
#[test]
fn public_codec_preserves_unknown_enum_payloads_and_rejects_invalid_inputs() {
let descriptor = TypeDescriptor::enumeration([VariantDescriptor::new(
"Known",
VariantPayload::Unit,
None,
)])
.unwrap();
let raw = br#"{"tag":"Future","payload":{"z":1,"a":true}}"#;
let decoded = decode(raw, &descriptor, DecodeRole::ConsumerOutput, limits(raw)).unwrap();
assert_eq!(encode(&decoded, &descriptor).unwrap(), raw);
assert_eq!(
encode(&SlotValue::Missing, &TypeDescriptor::bool())
.unwrap_err()
.kind(),
EncodeErrorKind::MissingValue
);
let Err(DecodeError::Semantic(error)) = decode(
b"true",
&TypeDescriptor::string(),
DecodeRole::ProviderInput,
Limits::new(4, 1),
) else {
panic!("wrong representations must be rejected")
};
assert_eq!(error.kind(), SemanticErrorKind::RepresentationMismatch);
assert!(matches!(
decode(
b"[[]]",
&TypeDescriptor::list(TypeDescriptor::list(TypeDescriptor::bool()).unwrap())
.unwrap(),
DecodeRole::ProviderInput,
Limits::new(4, 1)
),
Err(DecodeError::Syntax(SyntaxError::DepthLimitExceeded {
limit: 1
}))
));
let opaque = SlotValue::Value(V::opaque(OpaquePayload::new(OpaqueTree::Null)));
assert_eq!(
encode(&opaque, &TypeDescriptor::bool()).unwrap_err().kind(),
EncodeErrorKind::RepresentationMismatch
);
let descriptor =
TypeDescriptor::secret(TypeDescriptor::optional(TypeDescriptor::string()).unwrap())
.unwrap();
let decoded = decode(
b"null",
&descriptor,
DecodeRole::ProviderInput,
Limits::new(4, 1),
)
.unwrap();
assert_eq!(decoded, SlotValue::Value(V::sensitive(V::null())));
assert_eq!(encode(&decoded, &descriptor).unwrap(), b"null");
}
}