Trait Base32SnowExt

Source
pub trait Base32SnowExt: SnowflakeId
where Self::Ty: BeBytes,
{ // Provided methods fn buf() -> <<Self as Id>::Ty as BeBytes>::Base32Array { ... } fn encode(&self) -> Base32SnowFormatter<Self> { ... } fn encode_to_buf<'buf>( &self, buf: &'buf mut <<Self as Id>::Ty as BeBytes>::Base32Array, ) -> Base32SnowFormatterRef<'buf, Self> { ... } fn decode(s: impl AsRef<str>) -> Result<Self, Error<Self>> { ... } }
Expand description

Extension trait for Crockford Base32 encoding and decoding of ID types.

This trait enables converting IDs backed by integer types into fixed-length, lexicographically sortable Base32 representation using the Crockford Base32 alphabet.

Provided Methods§

Source

fn buf() -> <<Self as Id>::Ty as BeBytes>::Base32Array

Returns a stack-allocated, zero-initialized buffer for Base32 encoding.

This is a convenience method that returns a BeBytes::Base32Array suitable for use with Base32SnowExt::encode_to_buf. The returned buffer is stack-allocated, has a fixed size known at compile time, and is guaranteed to match the Crockford Base32 output size for the backing integer type.

See also: Base32SnowExt::encode_to_buf for usage.

Source

fn encode(&self) -> Base32SnowFormatter<Self>

Returns a formatter containing the Crockford Base32 representation of the ID.

The formatter is a lightweight, zero-allocation view over that internal buffer that implements core::fmt::Display and AsRef<str>.

§Example
#[cfg(all(feature = "base32", feature = "snowflake"))]
{
    use ferroid::{Base32SnowExt, SnowflakeTwitterId};
    use std::fmt::Write;

    let id = SnowflakeTwitterId::from_raw(2_424_242_424_242_424_242);

    // Formatter is a view over the internal encoded buffer
    let formatter = id.encode();

    assert_eq!(formatter, "23953MG16DJDJ");
}
Source

fn encode_to_buf<'buf>( &self, buf: &'buf mut <<Self as Id>::Ty as BeBytes>::Base32Array, ) -> Base32SnowFormatterRef<'buf, Self>

Encodes this ID into the provided buffer without heap allocation and returns a formatter view over the buffer similar to Base32SnowExt::encode.

The buffer must be exactly BeBytes::BASE32_SIZE bytes long, which is guaranteed at compile time when using Base32SnowExt::buf.

§Example
#[cfg(all(feature = "base32", feature = "snowflake"))]
{
    use ferroid::{Base32SnowExt, BeBytes, Id, SnowflakeTwitterId};

    let id = SnowflakeTwitterId::from_raw(2_424_242_424_242_424_242);

    // Stack-allocated buffer of the correct size.
    let mut buf = SnowflakeTwitterId::buf();

    // Formatter is a view over the external buffer
    let formatter = id.encode_to_buf(&mut buf);

    assert_eq!(formatter, "23953MG16DJDJ");

    // Or access the raw bytes directly:
    let as_str = unsafe { core::str::from_utf8_unchecked(buf.as_ref()) };
    assert_eq!(as_str, "23953MG16DJDJ");
}

See also: Base32SnowExt::encode for a version that manages its own buffer.

Source

fn decode(s: impl AsRef<str>) -> Result<Self, Error<Self>>

Decodes a Base32-encoded string back into an ID.

⚠️ Note:
This method structurally decodes a Crockford base32 string into an integer representing a Snowflake ID, regardless of whether the input is a canonical Snowflake ID.

  • If the input string’s Crockford encoding is larger than the Snowflake’s maximum (i.e. “FZZZZZZZZZZZZ” for 64-bit integers), the excess bit is automatically ignored (i.e., the top 1 bit of the decoded value is discarded), so no overflow or error occurs.
  • As a result, base32 strings that are technically invalid (i.e., lexicographically greater than the max Snowflake string) will still successfully decode.
  • However, if your ID type reserves bits (e.g., reserved or unused bits in your layout), decoding a string with excess bits may set these reserved bits to 1, causing .is_valid() to fail, and decode to return an error.
§Errors

Returns an error if the input string:

  • is not the expected fixed length of the backing integer representation (i.e. 13 chars for u64, 26 chars for u128)
  • contains invalid ASCII characters (i.e., not in the Crockford Base32 alphabet)
  • sets reserved bits that make the decoded value invalid for this ID type
§Example
#[cfg(all(feature = "base32", feature = "snowflake"))]
{
   use ferroid::{Base32Error, Base32SnowExt, Error, Id, SnowflakeId, SnowflakeTwitterId};

   // Crockford Base32 encodes values in 5-bit chunks, so encoding a u64
   // (64 bits)
   // requires 13 characters (13 × 5 = 65 bits). Since u64 can only hold 64
   // bits, the highest (leftmost) bit is discarded during decoding.
   //
   // This means *any* 13-character Base32 string will decode into a u64, even
   // if it represents a value that exceeds the canonical range of a specific
   // ID type.
   //
   // Many ID formats (such as Twitter Snowflake IDs) reserve one or more high
   // bits for future use. These reserved bits **must remain unset** for the
   // decoded value to be considered valid.
   //
   // For example, in a `SnowflakeTwitterId`, "7ZZZZZZZZZZZZ" represents the
   // largest lexicographically valid encoding that fills all non-reserved bits
   // with ones. Lexicographically larger values like "QZZZZZZZZZZZZ" decode to
   // the *same* ID because their first character differs only in the highest
   // (65th) bit, which is discarded:
   // - '7' = 0b00111 → top bit 0, reserved bit 0, rest = 111...
   // - 'Q' = 0b10111 → top bit 1, reserved bit 0, rest = 111...
   //            ↑↑↑↑ identical after discarding MSB
   let id1 = SnowflakeTwitterId::decode("7ZZZZZZZZZZZZ").unwrap();
   let id2 = SnowflakeTwitterId::decode("QZZZZZZZZZZZZ").unwrap();
   assert_eq!(id1, id2);

   // In contrast, "PZZZZZZZZZZZZ" differs in more significant bits and decodes
   // to a distinct value:
   // - 'P' = 0b10110 → top bit 1, reserved bit 0, rest = 110...
   //               ↑ alters bits within the ID layout beyond the reserved region
   let id3 = SnowflakeTwitterId::decode("PZZZZZZZZZZZZ").unwrap();
   assert_ne!(id1, id3);

   // If the reserved bits are set (e.g., 'F' = 0b01111 or 'Z' = 0b11111),
   // decoding fails and the invalid ID is returned:
   // - 'F' = 0b01111 → top bit 0, reserved bit 1, rest = 111...
   //            ↑ reserved bit is set - ID is invalid
   let id = SnowflakeTwitterId::decode("FZZZZZZZZZZZZ")
       .or_else(|err| {
           match err {
               Error::Base32Error(Base32Error::DecodeOverflow { id }) => {
                   debug_assert!(!id.is_valid());
                   // clears reserved bits
                   let valid = id.into_valid();
                   debug_assert!(valid.is_valid());
                   Ok(valid)
               }
               other => Err(other),
           }
       })
       .expect("should produce a valid ID");
}

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.

Implementors§

Source§

impl<ID> Base32SnowExt for ID
where ID: SnowflakeId, ID::Ty: BeBytes,