ulid-rs

repository·master·Indexed 19 days ago

https://github.com/dylanhart/ulid-rs

A Rust implementation of the Universally Unique Lexicographically Sortable Identifier (ULID) specification (version 3.0.0). It provides the Ulid struct for generating unique, time-sortable 128-bit identifiers, a Generator for monotonically increasing IDs, and a CLI tool for generation and inspection. Features include support for #[no_std], serde serialization, and infallible conversions to UUIDs.

Tokens
3.6K
Snippets
16
Records
19
Agent score
65%

What's inside ulid-rs

  1. Quickstart with ulid-rs

    master

    To use ulid-rs in your Rust project, import the Ulid struct. You can generate new identifiers, convert them to strings, and parse them back from strings.

    use ulid::Ulid;
    
    // Generate a ulid
    let ulid = Ulid::generate();
    
    // Generate a string for a ulid
    let s = ulid.to_string();
    
    // Create from a String
    let res = Ulid::from_string(&s);
    
    assert_eq!(ulid, res.unwrap());
  2. The Ulid struct

    master

    A Ulid is a unique 128-bit lexicographically sortable identifier. It is canonically represented as a 26-character Crockford Base32 encoded string.

    The 128 bits are divided into:

    • 48 bits: A Unix timestamp in milliseconds (provides lexicographic sorting).
    • 80 bits: Random data (ensures uniqueness).

    Constants:

    • Ulid::TIME_BITS: 48
    • Ulid::RAND_BITS: 80
  3. Configure ulid-rs crate features

    master

    The ulid crate provides several optional features to customize its behavior:

    • std (default): Enables the use of std and rand. Disable this flag if you require #[no_std] support.
    • serde: Enables serialization and deserialization of Ulid types via serde. ULIDs are serialized using their canonical 26-character representation. It also provides an optional ulid_as_u128 module for serializing through the inner u128 primitive.
    • uuid: Enables infallible conversions between ULIDs and UUIDs from the uuid crate using the std::convert::From trait.
  4. Handle ULID overflow errors in `Generator`

    master

    When using Generator::generate(), if the random bits of the ULID overflow (meaning the maximum value for the current millisecond has been reached), the method returns an Overflow error.

    To recover and continue generating monotonic ULIDs, you must "commit" the overflow. You have two primary strategies:

    1. commit_overflow_increment(): Increments the ULID into the next millisecond, starting the random bits at zero.
    2. commit_overflow_random(): Increments the ULID into the next millisecond, but starts the random bits with a new random value.

    This ensures the generator state is updated and the next call will succeed.

    use ulid::Generator;
    
    let mut generator = Generator::new();
    
    let ulid = match generator.generate() {
        Ok(ulid) => ulid,
        // If random bits overflow, commit the overflow to move to the next millisecond
        Err(overflow) => overflow.commit_overflow_increment(),
    };
  5. Generate and manipulate ULIDs

    master

    The Ulid struct is the primary interface for working with Universally Unique Lexicographically Sortable Identifiers.

    Key operations include:

    • Ulid::generate(): Creates a new ULID.
    • ulid.to_string(): Converts the ULID to its canonical 26-character string representation.
    • Ulid::from_string(&str): Parses a string into a Ulid instance.
    use ulid::Ulid;
    
    let ulid = Ulid::generate();
    let s = ulid.to_string();
    let res = Ulid::from_string(&s);
  6. Decode a Base32 ULID string to a u128

    master

    Convert a 26-character Base32 encoded string back into its original u128 value using decode. This function is a const fn and supports case-insensitive decoding (both uppercase and lowercase characters are valid).

    Possible errors:

    • DecodeError::InvalidLength: The input string is not exactly 26 characters long.
    • DecodeError::InvalidChar: The string contains characters not present in the ULID Base32 alphabet.
    let encoded = "21850M2GA1850M2GA1850M2GA1";
    let val = decode(encoded).unwrap();
    assert_eq!(val, 0x41414141414141414141414141414141);
  7. Extract timestamp and random components from a Ulid

    master

    You can retrieve the constituent parts of a Ulid using these methods:

    • ulid.timestamp_ms() -> u64: Returns the 48-bit timestamp portion.
    • ulid.random() -> u128: Returns the 80-bit random portion.
    use ulid::Ulid;
    
    let ulid = Ulid::from_string("01D39ZY06FGSCTVN4T2V9PKHFZ").unwrap();
    let ts = ulid.timestamp_ms();
    let rand = ulid.random();
  8. Encode a u128 value into a fixed-size byte array

    master

    For performance-critical or no_std environments, use encode_to_array. This function encodes a u128 directly into a provided mutable byte array of size ULID_LEN. This is a const fn and can be used in constant contexts.

    let mut buffer: [u8; ULID_LEN] = [0; ULID_LEN];
    let val: u128 = 0x41414141414141414141414141414141;
    encode_to_array(val, &mut buffer);
  9. Generate monotonically increasing ULIDs with `Generator`

    master

    The Generator struct provides a way to generate ULIDs that are guaranteed to be monotonically increasing (each call returns a ULID larger than the previous one). This is useful for maintaining sort order when generating IDs in rapid succession or within the same millisecond.

    To use it, create a new generator and call generate(). If the random bits of the current ULID reach their maximum capacity within the same millisecond, the generator will return an Overflow error, which must be handled to continue generating monotonic IDs.

    use ulid::Generator;
    
    let mut generator = Generator::new();
    
    let ulid1 = generator.generate().unwrap();
    let ulid2 = generator.generate().unwrap();
    
    assert!(ulid1 < ulid2);
  10. Convert Ulid to and from bytes and integers

    master

    The Ulid type implements several conversion traits:

    To other types:

    • Into<u128>: Converts to the underlying 128-bit integer.
    • Into<[u8; 16]>: Converts to a big-endian byte array.
    • Into<String>: Converts to a Base32 string (requires std).
    • Into<(u64, u64)>: Converts to a tuple of two 64-bit integers.

    From other types:

    • From<u128>
    • From<[u8; 16]>
    • From<(u64, u64)>
    use ulid::Ulid;
    
    let ulid = Ulid::generate();
    
    // To u128
    let val: u128 = ulid.into();
    
    // To bytes
    let bytes: [u8; 16] = ulid.into();
    
    // From bytes
    let ulid_from_bytes = Ulid::from(bytes);
  11. Generate ULIDs with specific timestamps or random sources

    master

    The Generator provides methods to control the timestamp and the entropy source used for generation:

    • generate_from_datetime(SystemTime): Generates a monotonic ULID using the provided SystemTime.
    • generate_with_source<R>(&mut R): Generates a monotonic ULID using a provided random number generator (RNG) that implements rand::Rng.
    • generate_from_datetime_with_source(SystemTime, &mut R): Combines both, using a specific timestamp and a specific entropy source.

    These methods are useful for deterministic testing or when you need to synchronize ULID generation with a specific clock.

    use ulid::Generator;
    use std::time::SystemTime;
    use rand::prelude::StdRng;
    
    let mut generator = Generator::new();
    let dt = SystemTime::now();
    let mut rng = StdRng::from_entropy();
    
    // Using a specific datetime
    let ulid = generator.generate_from_datetime(dt).unwrap();
    
    // Using a specific source
    let ulid_with_rng = generator.generate_with_source(&mut rng).unwrap();
    
    // Using both
    let ulid_custom = generator.generate_from_datetime_with_source(dt, &mut rng).unwrap();
  12. Increment a Ulid's random component

    master

    The increment() method increases the random portion of the ULID by 1 while keeping the timestamp identical.

    Warning: If the random component is already at its maximum value (bitmask!(80)), the function will return an Err containing the overflowed ULID (where the timestamp has been incremented).

    use ulid::Ulid;
    
    let ulid = Ulid::from_string("01BX5ZZKBKAZZZZZZZZZZZZZZ").unwrap();
    
    match ulid.increment() {
        Ok(next_ulid) => println!("Next: {}", next_ulid),
        Err(overflowed) => println!("Overflowed to: {}", overflowed),
    }