Skip to content

fluo10/mtid

Repository files navigation

MTID (Multi-length Triplet ID)

A human-friendly identifier format based on 3-character blocks ("triplet"). This crate provides multiple fixed-length variants:

  • Stid: Single triplet ID (e.g. 123)
  • Dtid: Double Triplet ID (e.g. 456-789)
  • Ttid: Triple Triplet ID (e.g. abc-def-ghj)
  • Qtid: Quadruple triplet ID (e.g. kmn-pqr-stv-wxy)

For a language agnostic specification of the MTID format, see SPECS.md

Quick Start

use mtid::Dtid;

let id = Dtid::random();
println!("{}", id); // e.g. "1a2-b3c"

Why MTID?

Traditional identifier systems face challenges in distributed environments:

  • Sequential numbers (like GitHub issue numbers) cause collisions in distributed systems
  • UUIDs are too long and not human-friendly
  • Short hashes (like Git commit hashes) lack standardization

MTID bridges the gap between human readability and technical requirements.

Which length should I use?

  • DTID(Double length triplet ID) is recommended for the personal data because this is short enough to satisfy the Magic Number 7±2 principle and have enough range of value (for the data entered manually by individuals (such as pocketbooks, journals, or activity logs)).
  • STID(Single length triplet ID) is recommended if the data is expected to be so few that they can be counted.
  • TTID(Triple length triplet ID) is recommended if it is expected that one or more data will be added every second.
  • QTID(Quadruple length Triplet ID) is recommended if, the number of data could potentially become so large that it's impossible to predict (for example, in a multi-user application where the IDs must be unique across users).

Installation

Add this to your Cargo.toml:

[dependencies]
mtid = "0.6"

# With optional features
mtid = { version = "0.6", features = ["arbitrary", "serde", "rusqlite", "sea-orm", "prost"] }

For no_std Environments

This crate support no_std. For no_std environment, you'll need to disable default features.

[dependencies]
mtid = { version = "0.6", default-features = false }

Features

  • Human-friendly: Easy to read, type, and communicate
  • Collision-resistant: Sufficient entropy for distributed systems
  • Compact: Shorter than UUIDs while maintaining uniqueness
  • Type-safe: Rust implementation with strong typing
  • Multiple integrations: Support for serde, rusqlite, sea-orm, and protobuf

Optional Feature Flags

  • arbitrary: arbitrary::Arbitrary support for fuzzing tests.
  • serde: Serialization/deserialization support
  • rusqlite: SQLite database integration
  • sea-orm: SeaORM ORM integration
  • prost: Protocol Buffers support

Examples

use mtid::{Stid, Dtid, Ttid, Qtid};
// Generate random MTID
let stid = Stid::random();
let dtid = Dtid::random();
let ttid = Ttid::random();
let qtid = Qtid::random();

// '123', '456-789', 'abc-def-ghj', 'kmn-pqr-stv-wxy'
println!("'{}', '{}', '{}'. '{}'", stid, dtid, ttid, qtid);

// Parse from string
let valid_id: Dtid = "012-tvw".parse()?;

// The code without delimiter is valid.
let valid_id_without_delimiter: Dtid = "012tvw".parse()?;
assert_eq!(valid_id, valid_id_without_delimiter);

// When decoding from BASE32, ambiguous characters (1/l/I, 0/o, v/u, -/_) are treated as 1, 0, v, and - respectively, so they do not cause errors.
let also_valid_id: Dtid = "ol2_tuw".parse()?;
assert_eq!(valid_id, also_valid_id);

// Convert to/from integer
let num: u32 = valid_id.into();
let id_from_int: Dtid = num.try_into()?;
assert_eq!(valid_id, id_from_int);

// Lossy conversion from oversized int is allowed.
let id_from_overflowed_int = Dtid::from_uint_lossy(Dtid::CAPACITY + num);
assert_eq!(valid_id, id_from_overflowed_int);

License

Licensed under either of:

at your option.

About

Human-friendly id for personal use distributed system

Topics

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Stars

Watchers

Forks

Contributors 2

  •  
  •  

Languages