Skip to main content

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}