planck_pack_core/traits.rs
1use crate::error::DecodeError;
2
3/// A type that can be packed into a mixed-radix representation.
4///
5/// Each implementor declares how many distinct values it can take ([`RADIX`](Self::RADIX))
6/// and provides bidirectional mapping between values and ordinals in `[0, RADIX)`.
7///
8/// Planck uses these ordinals to encode structs as mixed-radix numbers: each field
9/// becomes a digit with its own base. The total number of bits needed is
10/// `⌈log₂(r₁ × r₂ × ... × rₙ)⌉`, which is always ≤ the sum of individual field bit widths.
11///
12/// # Derive
13///
14/// The easiest way to implement this trait is via `#[derive(Planck)]` from the `planck` crate.
15///
16/// # Manual Implementation
17///
18/// ```
19/// use planck_pack_core::{Packable, DecodeError};
20///
21/// struct DieRoll(u8); // 1-6
22///
23/// impl Packable for DieRoll {
24/// const RADIX: u128 = 6;
25///
26/// fn to_ordinal(&self) -> u128 {
27/// (self.0 - 1) as u128
28/// }
29///
30/// fn from_ordinal(ord: u128) -> Result<Self, DecodeError> {
31/// if ord < 6 {
32/// Ok(DieRoll(ord as u8 + 1))
33/// } else {
34/// Err(DecodeError::OrdinalOutOfRange { ordinal: ord, radix: 6 })
35/// }
36/// }
37/// }
38///
39/// assert_eq!(DieRoll(3).to_ordinal(), 2);
40/// assert_eq!(DieRoll::RADIX, 6);
41/// ```
42pub trait Packable: Sized {
43 /// The number of distinct values this type can take.
44 ///
45 /// For a `bool` this is 2, for an enum with 3 variants this is 3,
46 /// for a `u8` constrained to `0..=10` this is 11.
47 ///
48 /// For structs, `RADIX` is the product of all field radixes.
49 /// For enums, `RADIX` is the sum of all variant radixes.
50 const RADIX: u128;
51
52 /// Convert this value to its ordinal position in `[0, RADIX)`.
53 ///
54 /// The returned value must always be less than [`RADIX`](Self::RADIX).
55 fn to_ordinal(&self) -> u128;
56
57 /// Reconstruct from an ordinal. Returns `Err` if `ord >= RADIX`.
58 fn from_ordinal(ord: u128) -> Result<Self, DecodeError>;
59}