tnid 0.0.8

A UUID compatible ID with static type checking
Documentation

TNID

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.

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

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

// Create a time-ordered ID (like UUIDv7)
let user_id = Tnid::<User>::new_time_ordered();
println!("{}", user_id);  // user.Br2flcNDfF6LYICnT

// Or a high-entropy ID (like UUIDv4)
let session_id = Tnid::<User>::new_high_entropy();

Why TNIDs?

  • Type-safe: Tnid<User> and Tnid<Post> are different types. Accidentally passing a post ID to a user function? Compile error!
  • Named: IDs include a human-readable name prefix. See user.Br2flcNDfF6LYICnT in your logs and instantly know what it is.
  • UUID-compatible: TNIDs are valid UUIDv8s that work directly with Postgres UUID columns and UUID-expecting APIs.
  • Compile-time validated: Try to create a TNID with name "INVALID"? Your code won't even compile.
  • Sortable strings: Unlike UUID hex (case-insensitive mess), TNID strings sort correctly and have exactly one representation.

Status

⚠️ Beta: The TNID spec is still being finalized and shouldn't be relied on for production use yet. This implementation tracks the evolving spec.

A full specification site will be available at tnid.info.

Installation

cargo add tnid

Examples

Creating TNIDs

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

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

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

// High-entropy (v1) - maximum randomness
let id = Tnid::<Post>::new_v1();

String Representations

// TNID string format - human-readable, sortable, unambiguous
let tnid_str = id.to_tnid_string();
// "post.Br2flcNDfF6LYICnT" - you can SEE it's a post ID!

// UUID hex format - for databases and APIs that expect UUIDs
let uuid_str = id.to_uuid_string(Case::Lower);
// "cab1952a-f09d-86d9-928e-96ea03dc6af3" - works in Postgres, MySQL, etc.

// 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());  // Sorts correctly!
assert!(id1.as_u128() < id2.as_u128());                // In all representations!

Parsing

// Parse from TNID string
let id = Tnid::<Post>::parse_tnid_string("post.Br2flcNDfF6LYICnT").unwrap();

// Parse from UUID string
let id = Tnid::<Post>::parse_uuid_string("cab1952a-f09d-86d9-928e-96ea03dc6af3").unwrap();

// From raw u128
let id = Tnid::<Post>::from_u128(0xCAB1952A_F09D_86D9_928E_96EA03DC6AF3).unwrap();

Type Safety in Action

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(user_id: Tnid<User>) { /* ... */ }
fn delete_post(post_id: Tnid<Post>) { /* ... */ }

let user_id = Tnid::<User>::new_v0();
let post_id = Tnid::<Post>::new_v0();

delete_user(user_id);  // Works!
delete_post(post_id);  // Works!

// delete_user(post_id);  // Compile error! Can't pass a Post ID to a User function
// delete_post(user_id);  // Compile error! Type mismatch caught at compile time

Features

Feature Default Ready Description
time stable Time-based v0 TNID generation (like UUIDv7)
rand stable Random v1 TNID generation (like UUIDv4)
encryption beta Encrypt v0 to v1 to hide timestamps from clients, decrypt on the backend
uuid stable Convert to/from the uuid crate's Uuid type
serde alpha serde::Serialize / serde::Deserialize for Tnid<Name>, DynamicTnid, UuidLike
sqlx-postgres alpha SQLx Type/Encode/Decode for Postgres UUID columns
sqlx-mysql alpha SQLx Type/Encode/Decode for MySQL/MariaDB (BINARY/BLOB/TEXT)
sqlx-sqlite alpha SQLx Type/Encode/Decode for SQLite (BLOB/TEXT)

Documentation

See the API documentation for complete details.

License

MIT