Skip to main content

Crate tnid

Crate tnid 

Source
Expand description

UUID-compatible IDs with names and compile-time type safety.

TNIDs are UUIDv8-compatible identifiers that include a human-readable name and can be strictly typed at compile time. Tnid<User> and Tnid<Post> are distinct types, so accidentally passing one where the other is expected is a compile error.

§Quick Start

use tnid::{NameStr, Tnid, TnidName};

struct User;
impl TnidName for User {
    const ID_NAME: NameStr<'static> = NameStr::new_const("user");
}

// Time-ordered (v0) - sorts by creation time, like UUIDv7
let id = Tnid::<User>::new_v0();

// High-entropy (v1) - maximum randomness, like UUIDv4
let id = Tnid::<User>::new_v1();

§String Representations

Every TNID has two string forms:

let id = Tnid::<Post>::new_v0();

// TNID string - human-readable, sortable, unambiguous
let tnid_str = id.to_tnid_string();
// e.g. "post.Br2flcNDfF6LYICnT"

// UUID hex string - for databases and APIs expecting UUIDs
let uuid_str = id.to_uuid_string(Case::Lower);
// e.g. "cab1952a-f09d-86d9-928e-96ea03dc6af3"

Time-ordered IDs sort correctly in both representations:

let id1 = Tnid::<Post>::new_v0();
std::thread::sleep(std::time::Duration::from_millis(10));
let id2 = Tnid::<Post>::new_v0();

assert!(id1.to_tnid_string() < id2.to_tnid_string());
assert!(id1.as_u128() < id2.as_u128());

§Parsing

// From a TNID string
let id = Tnid::<Post>::new_v0();
let parsed = Tnid::<Post>::parse_tnid_string(&id.to_tnid_string()).unwrap();
assert_eq!(id, parsed);

// From a UUID string
let parsed = Tnid::<Post>::parse_uuid_string(&id.to_uuid_string(tnid::Case::Lower)).unwrap();
assert_eq!(id, parsed);

// From a raw u128
let parsed = Tnid::<Post>::from_u128(id.as_u128()).unwrap();
assert_eq!(id, parsed);

§Type Safety

struct User;
impl TnidName for User {
    const ID_NAME: NameStr<'static> = NameStr::new_const("user");
}

struct Post;
impl TnidName for Post {
    const ID_NAME: NameStr<'static> = NameStr::new_const("post");
}

fn delete_user(id: Tnid<User>) { /* ... */ }

let post_id = Tnid::<Post>::new_v0();
delete_user(post_id); // Compile error: expected Tnid<User>, got Tnid<Post>

§Features

FeatureDefaultStatusDescription
timeyesstableTime-based v0 generation (Tnid::new_v0)
randyesstableRandom v1 generation (Tnid::new_v1)
filterbetaGenerate IDs without blocklisted substrings
encryptionbetaEncrypt v0 to v1 to hide timestamps
uuidstableConvert to/from the uuid crate
serdealphaSerialize/deserialize support
sqlx-postgresalphaSQLx support for Postgres UUID columns
sqlx-mysqlalphaSQLx support for MySQL/MariaDB
sqlx-sqlitealphaSQLx support for SQLite

Re-exports§

pub use dynamic_tnid::DynamicTnid;

Modules§

dynamic_tnid
Runtime-determined TNIDs without compile-time type checking.
encryptionencryption
TNID encryption utilities.
filterfilter
Filtering support for generating TNIDs without blocklisted words.
internalsinternals
Internal functions exposed for advanced usage behind the internals feature.

Structs§

NameStr
A validated TNID name string.
Tnid
A type-safe TNID parameterized by name.
UuidLike
A wrapper for 128-bit values that may or may not be valid TNIDs (or UUIDs for that matter).

Enums§

Case
The case (upper/lower) for hexadecimal string formatting.
DataEncodingError
Error when decoding a TNID data string.
NameBitsValidation
Result of validating the name bits in a TNID.
NameError
Error when creating a NameStr from a string.
ParseTnidError
Error when parsing a TNID from a string or u128.
ParseUuidStringError
Error when parsing a UUID string.
TnidVariant
The 4 possible TNID variants.

Traits§

TnidName
Intended to be used on empty structs to create type checked TNID names.