entid
A Rust library for generating and validating type-safe, prefixed entity identifiers based on UUIDs and ULIDs.
Features
- Type-safe entity IDs: Create distinct ID types for different entities
- Multiple identifier formats: Support for both UUID and ULID
- Prefix support: Automatically add entity-specific prefixes to IDs
- Performance optimized: Thread-safe caching of string representations
- Serde compatible: Seamless serialization and deserialization
- Comprehensive error handling: Clear error types for all operations
- Zero-cost abstractions: Minimal runtime overhead
- Derive macro for implementing the
Prefixtrait: Optional
Installation
Add this to your Cargo.toml:
[]
= "0.4.3"
To use the derive macro for implementing the Prefix trait, enable the derive feature:
[]
= { = "0.4.3", = ["derive"] }
API Overview
The EntityId type provides several methods for working with entity IDs:
type UserId = ;
// Create a new EntityId
let user_id = generate;
// Get the full ID string with prefix (e.g., "user_123e4567-e89b-12d3-a456-426614174000")
let full_id = user_id.as_str;
// Get just the identifier part without the prefix (e.g., "123e4567-e89b-12d3-a456-426614174000")
let raw_id = user_id.id_str;
// Get a reference to the underlying identifier object
let identifier = user_id.identifier;
// Get the identifier string directly from the identifier
let id_str = user_id.identifier.as_str;
// Get the prefix for this entity type
let prefix = prefix; // "user"
// Get the delimiter for this entity type
let delimiter = delimiter; // "_"
// For ULID-based IDs, get the timestamp
if let Some = ulid_id.timestamp_ms
Flexible Creation Methods
The library provides multiple ways to create entity IDs:
use ;
type UserId = ;
// Using the generate method
let user_id1 = generate;
// Using the new method with flexible string types (with prefix)
let id_str = "user_123e4567-e89b-12d3-a456-426614174000";
let user_id2 = new.unwrap;
let user_id3 = new.unwrap;
// Using from_raw_str to parse a raw identifier string (without prefix)
let raw_uuid = "123e4567-e89b-12d3-a456-426614174000";
let user_id4 = from_raw_str.unwrap;
// Using parse_raw_str with custom error handling
let user_id5 = parse_raw_str.unwrap;
// Using TryFrom trait
let user_id6 = try_from.unwrap;
let user_id7 = try_from.unwrap;
// Using FromStr trait
let user_id8 = id_str..unwrap;
// Using convenience methods
let uuid = new_v4;
let user_id9 = with_uuid;
let user_id10 = new_v4;
let user_id11 = new_v5;
// Using the builder pattern
let user_id12 = builder.build;
let user_id13 = builder.with_uuid.build;
let user_id14 = builder.with_uuid_v4.build;
let user_id15 = builder.with_uuid_v5.build;
// For ULID-based IDs
type PostId = ;
let ulid = new;
let post_id1 = with_ulid;
let post_id2 = with_timestamp; // July 1, 2021
let post_id3 = monotonic_from;
// Using the builder pattern for ULID
let post_id4 = builder.with_ulid.build;
let post_id5 = builder.with_timestamp.build;
let post_id6 = builder.with_monotonic_from.build;
Using EntityId in Collections
The EntityId type implements Borrow<str> and AsRef<str>, making it easy to use in collections:
use ;
// Use EntityId as a key in a HashMap
let mut user_map = new;
user_map.insert;
// Look up by string
let user = user_map.get;
// Use EntityId in a HashSet
let mut user_set = new;
user_set.insert;
// Check if a string is in the set
let contains = user_set.contains;
Usage
Basic Example with UUID
use ;
type UserId = ;
// Define your entity types with custom prefixes
;
type PostId = ;
;
Using the Derive Macro
With the derive feature enabled, you can use the derive macro to implement the Prefix trait:
use ;
type UserId = ;
;
type PostId = ;
;
type CommentId = ;
// The delimiter is optional and defaults to "_"
;
Using ULID Instead of UUID
use ;
type ProductId = ;
;
Using Deterministic UUIDs (v5)
use ;
type ApiKeyToken = ;
;
Enhanced Error Handling
The library provides detailed error information and convenient methods for error handling:
use ;
use Error;
;
type UserId = ;
// Convert errors to strings
// Get the specific error type
// Access the underlying error directly
// Use the standard Error trait methods
Error Handling
use ;
type UserId = ;
;
// Parse a raw identifier string
Using with Serde
use ;
use ;
;
type OrderId = ;
Enhanced Error Handling with String Conversions
The library provides convenient error handling with string conversions through AsRef<str> and Into<String> implementations:
use ;
type TaskId = ;
;
Custom Validation
use ;
type ApiKeyToken = ;
;
// Extend EntityId with custom validation logic
Additional Conversion Methods
The library provides additional methods for converting between different representations:
use ;
;
type UserId = ;
// Generate a new ID
let user_id = generate;
// Convert to raw identifier string (without prefix)
let raw_string = user_id.to_raw_string;
assert_eq!;
// Convert to the underlying identifier type
let uuid_identifier: UuidIdentifier = user_id.to_identifier;
assert_eq!;
// Use with functions that accept string types
// Works directly with EntityId thanks to AsRef<str>
process_string;
Choosing Between UUID and ULID
UUID Advantages
- Industry standard with wide adoption
- Multiple versions for different use cases (v1, v3, v4, v5)
- Well-supported in databases and other systems
ULID Advantages
- Lexicographically sortable (sorts by creation time)
- URL-safe (no special characters)
- Shorter string representation (26 characters vs 36 for UUID)
- Built-in timestamp component
Performance Considerations
- String representations are cached using
OnceLockfor thread-safe lazy initialization - The
EntityIdtype implementsHash,PartialEq, andEqfor efficient use in collections - Memory usage is optimized by using
PhantomDatafor type parameters
License
This project is licensed under the MIT License - see the LICENSE file for details.