Skip to main content

icydb_schema/
codec.rs

1//! Bounded current-form proposal transport.
2
3use crate::{
4    MAX_SCHEMA_FRAGMENT_BYTES, MAX_SCHEMA_PROPOSAL_BYTES, SchemaContractError, SchemaFragment,
5    SchemaProposal,
6};
7
8/// Encode one canonical schema fragment.
9///
10/// # Errors
11///
12/// Returns a typed contract or size error when the fragment is malformed or
13/// exceeds the frozen transport bound.
14pub fn encode_schema_fragment(fragment: &SchemaFragment) -> Result<Vec<u8>, SchemaContractError> {
15    fragment.validate()?;
16    let bytes = candid::encode_one(fragment).map_err(|_| SchemaContractError::Encode)?;
17    enforce_encoded_bound(bytes, MAX_SCHEMA_FRAGMENT_BYTES)
18}
19
20/// Decode one bounded current-form schema fragment.
21///
22/// # Errors
23///
24/// Returns a typed size, decoding, canonicalization, or nested-contract error.
25pub fn decode_schema_fragment(bytes: &[u8]) -> Result<SchemaFragment, SchemaContractError> {
26    ensure_input_bound(bytes, MAX_SCHEMA_FRAGMENT_BYTES)?;
27    let fragment =
28        candid::decode_one::<SchemaFragment>(bytes).map_err(|_| SchemaContractError::Decode)?;
29    fragment.validate()?;
30    let canonical = encode_schema_fragment(&fragment)?;
31    if canonical != bytes {
32        return Err(SchemaContractError::NonCanonical);
33    }
34    Ok(fragment)
35}
36
37/// Encode one canonical database-scoped schema proposal.
38///
39/// # Errors
40///
41/// Returns a typed contract or size error when the proposal is malformed or
42/// exceeds the frozen transport bound.
43pub fn encode_schema_proposal(proposal: &SchemaProposal) -> Result<Vec<u8>, SchemaContractError> {
44    proposal.validate_current()?;
45    let bytes = candid::encode_one(proposal).map_err(|_| SchemaContractError::Encode)?;
46    enforce_encoded_bound(bytes, MAX_SCHEMA_PROPOSAL_BYTES)
47}
48
49/// Decode one bounded current-form database-scoped proposal.
50///
51/// # Errors
52///
53/// Returns a typed size, decoding, version, canonicalization, or nested
54/// contract error. Obsolete forms are never translated.
55pub fn decode_schema_proposal(bytes: &[u8]) -> Result<SchemaProposal, SchemaContractError> {
56    ensure_input_bound(bytes, MAX_SCHEMA_PROPOSAL_BYTES)?;
57    let proposal =
58        candid::decode_one::<SchemaProposal>(bytes).map_err(|_| SchemaContractError::Decode)?;
59    proposal.validate_current()?;
60    let canonical = encode_schema_proposal(&proposal)?;
61    if canonical != bytes {
62        return Err(SchemaContractError::NonCanonical);
63    }
64    Ok(proposal)
65}
66
67const fn ensure_input_bound(bytes: &[u8], max: usize) -> Result<(), SchemaContractError> {
68    if bytes.len() > max {
69        return Err(SchemaContractError::EncodedTooLarge {
70            len: bytes.len(),
71            max,
72        });
73    }
74    Ok(())
75}
76
77fn enforce_encoded_bound(bytes: Vec<u8>, max: usize) -> Result<Vec<u8>, SchemaContractError> {
78    ensure_input_bound(&bytes, max)?;
79    Ok(bytes)
80}