Base32SnowExt

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>> { ... } }
Available on crate features base32 and snowflake only.
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
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
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
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,