Crates.io | mti |
lib.rs | mti |
version | |
source | src |
created_at | 2024-07-12 21:03:32.504329 |
updated_at | 2024-07-15 20:00:55.396232 |
description | A Rust library that implements type-safe, prefix-enhanced identifiers based on the TypeID Specification |
homepage | |
repository | https://github.com/GovCraft/mti |
max_upload_size | |
id | 1301436 |
size | 0 |
Welcome to mti
, a Rust crate that brings the power of type-safe, prefix-enhanced identifiers to your distributed
systems. Built on the TypeID Specification, mti
combines the uniqueness of
UUIDs with the readability and type safety of prefixed identifiers, offering a robust solution for managing identifiers
across your applications.
This crate implements version 0.3.0 of the TypeID Specification created and maintained by Jetify. I'm grateful for their work in developing and managing this specification.
Add mti
to your Cargo.toml
:
[dependencies]
mti = "1.0.5-beta.1"
Then, in your Rust code:
use mti::prelude::*;
// Create a MagicTypeId for a user
let user_id = "user".create_type_id::<V7>();
println!("New User ID: {}", user_id); // e.g., "user_01h455vb4pex5vsknk084sn02q"
// Parse an existing MagicTypeId
let order_id = MagicTypeId::from_str("order_01h455vb4pex5vsknk084sn02q").unwrap();
assert_eq!(order_id.prefix().as_str(), "order");
use mti::prelude::*;
// Create with UUIDv7 (sortable, recommended)
let product_id = "product".create_type_id::<V7>();
// Create with UUIDv4 (random)
let user_id = "user".create_type_id::<V4>();
// Create with custom suffix
let custom_suffix = TypeIdSuffix::new::<V7>();
let order_id = "order".create_type_id_with_suffix(custom_suffix);
use mti::prelude::*;
// Sanitized creation (always produces a valid prefix)
let sanitized_id = "Invalid Prefix!".create_type_id::<V7>();
assert!(sanitized_id.to_string().starts_with("invalidprefix_"));
// Strict creation (returns an error for invalid prefixes)
let result = "Invalid Prefix!".try_create_type_id::<V7>();
assert!(result.is_err());
use mti::prelude::*;
let id = "user".create_type_id::<V7>();
assert!(id.starts_with("user_"));
assert_eq!(id.len(), 31);
// Use in string comparisons
assert_eq!(id.as_str(), id.to_string());
use mti::prelude::*;
let id_str = "product_01h455vb4pex5vsknk084sn02q";
let magic_id = MagicTypeId::from_str(id_str).unwrap();
assert_eq!(magic_id.prefix().as_str(), "product");
assert_eq!(magic_id.suffix().to_string(), "01h455vb4pex5vsknk084sn02q");
// Extract UUID
let uuid = magic_id.suffix().to_uuid();
println!("Extracted UUID: {}", uuid);
When MagicTypeId
is created with a V7
UUID, it provides a natural sorting order:
UUIDv7
suffix. This means that identifiers
generated later will appear after those generated earlier.use std::str::FromStr;
use std::thread::sleep;
use std::time::Duration;
use mti::prelude::*;
use typeid_prefix::prelude::*;
use typeid_suffix::prelude::*;
let prefix1 = TypeIdPrefix::from_str("user").unwrap();
let prefix2 = TypeIdPrefix::from_str("admin").unwrap();
let id1 = MagicTypeId::new(prefix1.clone(), TypeIdSuffix::new::<V7>());
sleep(Duration::from_millis(10)); // Ensure different timestamps
let id2 = MagicTypeId::new(prefix1.clone(), TypeIdSuffix::new::<V7>());
let id3 = MagicTypeId::new(prefix2.clone(), TypeIdSuffix::from_str(&id2.suffix().to_string()).unwrap());
assert!(id1 < id2, "Expected id1 to be less than id2 due to earlier timestamp");
assert_eq!(id2.suffix(), id3.suffix(), "Suffixes for id2 and id3 should be the same");
assert!(id3 < id2, "Expected id3 to be less than id2 due to lexicographically smaller prefix when timestamps are equal");
MagicTypeId is versatile and can be applied in various scenarios:
let order_id = "order".create_type_id::<V7>();
// Send to another service: "order_01h455vb4pex5vsknk084sn02q"
let user_id = "user".create_type_id::<V7>();
// MagicTypeIds behave like strings
db.insert_user(user_id, user_data);
#[get("/users/{id}")]
async fn get_user(id: Path<MagicTypeId>) -> impl Responder {
// Retrieve user with id
}
let namespace = Uuid::parse_str("6ba7b810-9dad-11d1-80b4-00c04fd430c8").unwrap();
let name = "example.com";
let v5_uuid = Uuid::new_v5( & namespace, name.as_bytes());
let domain_id = MagicTypeId::new(
TypeIdPrefix::from_str("domain").unwrap(),
TypeIdSuffix::from(v5_uuid)
);
// Always produces the same ID for "example.com"
assert_eq!(domain_id.uuid_str().unwrap(), "cfbff0d1-9375-5685-968c-48ce8b15ae17");
log::info!("Processing order {}", "order".create_type_id::<V7>());
use mti::prelude::*;
struct UserId(MagicTypeId);
struct OrderId(MagicTypeId);
impl UserId {
fn new() -> Self {
Self("user".create_type_id::<V7>())
}
}
impl OrderId {
fn new() -> Self {
Self("order".create_type_id::<V7>())
}
}
// Compile-time type safety
fn process_user(id: UserId) { /* ... */ }
fn process_order(id: OrderId) { /* ... */ }
let user_id = UserId::new();
let order_id = OrderId::new();
process_user(user_id);
process_order(order_id);
// process_user(order_id); // This would cause a compile-time error!
use mti::prelude::*;
#[derive(Debug)]
struct User {
id: MagicTypeId,
name: String,
}
fn create_user(name: &str) -> User {
User {
id: "user".create_type_id::<V7>(),
name: name.to_string(),
}
}
// In your database operations
let user = create_user("Alice");
// MagicTypeId behaves like a string
db.insert(user.id, & user);
// Retrieving
let retrieved_id = MagicTypeId::from_str("user_01h455vb4pex5vsknk084sn02q").unwrap();
let retrieved_user = db.get::<User>(retrieved_id.to_string());
mti
is designed with performance and safety in mind:
TypeIdPrefix
and TypeIdSuffix
crates.While this crate is feature-complete and thoroughly tested, it is currently in beta to gather wider community feedback. I encourage you to use it in your projects and provide feedback. If you encounter any issues or have suggestions, please file them on my GitHub repository.
I welcome contributions! Please see my GitHub repository for issues, feature requests, and pull requests.
This project is licensed under either of
at your option.
I'm @rrrodzilla, a technologist with 30 years of industry experience. I'm a former SOA and cloud architect, and former Principal Technical Product Manager at AWS for the Rust Programming Language. Currently, I'm the owner and operator of Govcraft, building and consulting on Rust and AI solutions.
For more information, visit https://www.govcraft.ai