{"id":16926716,"url":"https://github.com/kbknapp/typed-oid","last_synced_at":"2026-06-30T09:31:13.585Z","repository":{"id":216392424,"uuid":"736724405","full_name":"kbknapp/typed-oid","owner":"kbknapp","description":"A Typed Object ID in Rust","archived":false,"fork":false,"pushed_at":"2024-06-24T19:22:05.000Z","size":188,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-12-29T04:30:13.972Z","etag":null,"topics":["object-id","oid","rust","uuid"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kbknapp.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":"kbknapp","patreon":"kbknapp"}},"created_at":"2023-12-28T17:33:31.000Z","updated_at":"2024-06-24T19:22:08.000Z","dependencies_parsed_at":"2025-06-12T08:02:15.551Z","dependency_job_id":"88c831f6-fc06-4e46-ae91-2c49e07185fb","html_url":"https://github.com/kbknapp/typed-oid","commit_stats":null,"previous_names":["kbknapp/typed-oid"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/kbknapp/typed-oid","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kbknapp%2Ftyped-oid","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kbknapp%2Ftyped-oid/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kbknapp%2Ftyped-oid/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kbknapp%2Ftyped-oid/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kbknapp","download_url":"https://codeload.github.com/kbknapp/typed-oid/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kbknapp%2Ftyped-oid/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34961543,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-30T02:00:05.919Z","response_time":92,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["object-id","oid","rust","uuid"],"created_at":"2024-10-13T20:31:08.727Z","updated_at":"2026-06-30T09:31:13.563Z","avatar_url":"https://github.com/kbknapp.png","language":"Rust","funding_links":["https://github.com/sponsors/kbknapp","https://patreon.com/kbknapp"],"categories":[],"sub_categories":[],"readme":"# Container Image Reference\n\n![Rust Version][rustc-image]\n[![crates.io][crate-image]][crate-link]\n[![Dependency Status][deps-image]][deps-link]\n\nA library for using and handling Typed Object IDs.\n\n\u003c!-- vim-markdown-toc GFM --\u003e\n\n* [About](#about)\n* [The Pitch](#the-pitch)\n* [The Anti-Pitch](#the-anti-pitch)\n* [Example](#example)\n* [Minimum Supported Rust Version (MSRV)](#minimum-supported-rust-version-msrv)\n* [License](#license)\n\n\u003c!-- vim-markdown-toc --\u003e\n\n## About\n\nAn Object ID (OID) is a base32hex (which is base32 with the extended hex\nalphabet; see [RFC4648] for details) encoded UUID. The UUID is either v4\n(random) or v7 (based on UNIX Epoch; see [draft RFC4122v17] for details) - this\nlibrary further qualifies the OID with a \"type\" which is a short alphanumeric\nprefix separated from the OID by a `-`. This library refers to Typed OIDs as\nTOIDs which are distinct from the OID counterparts which lack the \"type\"\nprefix.\n\nFor example `EXA-4GKFGPRVND4QT3PDR90PDKF66O`, by convention the prefix is three\nASCII characters, however that is not a hard constraint of TOIDs in general.\n\n## The Pitch\n\nTOIDs allow a \"human readable subject line\" in the form of the prefix, where\nactual data is a UUIDv7. This means while debugging or reviewing a system it's\ntrivial to determine if an incorrect TOID was passed in a particular location by\nlooking at the prefix. This isn't achievable with bare UUIDs or GUIDs due to\ntheir lacking of any typed identifiers.\n\nIn other words, without using TOIDs it's far too easy to incorrectly swap say a\n`UserID` with an `OrderID` if they are both just simple GUIDs. Where as a TOID\nwould make the mistake more easily identifiable or even programmatically\nimpossible.\n\nBase32hex encoding the UUID also allows compressing the data into a smaller and\nmore familiar format for humans, akin to a commit hash. Using the \"extended hex\nencoding\" adds the additional property that the encodings do not lose their\nsort order when compared bitwise.\n\nFinally, using a UUIDv7 enables index locality when used as database entries.\n\n## The Anti-Pitch\n\nThe downside to TOIDs is a layer of indirection when handling IDs and values,\nit's not immediately obvious that the TOIDs are a prefixed UUIDv7.\nAdditionally, the prefixes themselves must be controlled in some manner\nincluding migrated on changes which adds a layer of complexity at the\napplication layer.\n\nThere is also additional processing overhead compared to a bare UUID in order\nto encode/decode as well as handling the appending and removing the prefixes.\n\nHowever, we believe the drawbacks pale in comparison to the benefits derived\nfrom the format.\n\n## Example\n\n```rust,no_run\nuse typed_oid::{error::*, Oid, OidStr, OidPrefix};\nuse uuid::Uuid;\nuse anyhow::Result;\n\nfn main() -\u003e Result\u003c()\u003e {\n    // TOIDs come in two flavors, Oid\u003cT\u003e and `OidStr`.\n\n    // A Oid\u003cT\u003e is a TOID using a Rust types as the type, e.g. a true Typed OID\n    // These are less ergonomic, but more type-safe.\n    run_oid()?;\n\n    // A `OidStr` is a TOID using a bare string as the type, e.g. \"Stringly Typed OID\"\n    // These easier to use, but less type-safe.\n    run_oidstr()?;\n\n    Ok(())\n}\n\nfn run_oidstr() -\u003e Result\u003c()\u003e {\n    // OIDs can be created with a given prefix alone\n    #[cfg(feature = \"uuid_v4\")]\n    {\n        let oid = OidStr::new_v4(\"EXA\")?;\n        println!(\"OidStr from UUIDv4: {oid}\");\n    }\n    #[cfg(feature = \"uuid_v7\")]\n    {\n        let oid = OidStr::new_v7_now(\"EXA\")?;\n        println!(\"OidStr from UUIDv7: {oid}\");\n    }\n    // OIDs can also be created from the raw parts\n    let oid = OidStr::try_with_uuid(\"EXA\", \"b3cfdafa-3fec-41e2-82bf-ff881131abf1\")?;\n    println!(\"OidStr from UUID: {oid}\");\n\n    // OIDs can be parsed from strings, however the \"value\" must be a valid\n    // base32hex (no pad) encoded UUID\n    let oid: OidStr = \"EXA-4GKFGPRVND4QT3PDR90PDKF66O\".parse()?;\n    println!(\"OidStr from string: {oid}\");\n\n    // One can retrieve the various parts of the OID if needed\n    println!(\"Components of {oid}:\");\n    println!(\"\\tPrefix: {}\", oid.prefix());\n    println!(\"\\tValue: {}\", oid.value());\n    println!(\"\\tUUID: {}\", oid.uuid());\n\n    Ok(())\n}\n\nfn run_oid() -\u003e Result\u003c()\u003e {\n    // In order for a struct to be used as a type it must implement typed_oid::OidPrefix\n    #[derive(Debug)]\n    struct EXA;\n    impl OidPrefix for EXA {}\n\n    // We can create a new OID by generating a random UUID\n    #[cfg(feature = \"uuid_v4\")]\n    {\n        let oid: Oid\u003cEXA\u003e = Oid::new_v4();\n        println!(\"Oid\u003cEXA\u003e with new UUIDv4: {oid}\");\n    }\n    #[cfg(feature = \"uuid_v7\")]\n    {\n        let oid: Oid\u003cEXA\u003e = Oid::new_v7_now();\n        println!(\"Oid\u003cEXA\u003e with new UUIDv7: {oid}\");\n    }\n    // Or by giving a UUID\n    let oid: Oid\u003cEXA\u003e = Oid::try_with_uuid(\"b3cfdafa-3fec-41e2-82bf-ff881131abf1\")?;\n    println!(\"Oid\u003cEXA\u003e with new UUID: {oid}\");\n\n    // We can go the other direction and parse a string to a Oid\u003cEXA\u003e\n    let oid: Oid\u003cEXA\u003e = \"EXA-4GKFGPRVND4QT3PDR90PDKF66O\".parse()?;\n    println!(\"Oid\u003cEXA\u003e with from string: {oid}\");\n\n    // One can retrieve the various parts of the OID if needed\n    println!(\"Components of {oid}:\");\n    println!(\"\\tPrefix: {}\", oid.prefix());\n    println!(\"\\tValue: {}\", oid.value());\n    println!(\"\\tUUID: {}\", oid.uuid());\n\n    // However, if we change the prefix to something that doesn't match our EXA type\n    // we get an error even if the UUID is valid\n    let res = \"FAIL-4GKFGPRVND4QT3PDR90PDKF66O\".parse::\u003cOid\u003cEXA\u003e\u003e();\n    assert!(res.is_err());\n    assert_eq!(res.unwrap_err(), Error::InvalidPrefix { valid_until: 0 });\n\n    Ok(())\n}\n```\n\n## Minimum Supported Rust Version (MSRV)\n\nThe MSRV depends on which crate features are enabled:\n\n| Feature | MSRV |\n| :-: | :-: |\n| `uuid_4` | 1.60.0 |\n| `uuid_7` | 1.60.0 |\n| `serde` | 1.60.0 |\n| `surrealdb` | 1.75.0 |\n\n## License\n\nThis project is dual licensed under the terms of either the Apache License,\nVersion 2.0, [LICENSE-APACHE] or MIT [LICENSE-MIT] at your option.\n\n`typed-oid` was originally forked from [`seaplane-oid`][seaplane_oid] v0.4.0\nwhich was licensed under the Apache License, Version 2.0.\n\n[//]: # (badges)\n\n[crate-image]: https://img.shields.io/crates/v/typed-oid.svg\n[crate-link]: https://crates.io/crates/typed-oid\n[deps-image]: https://deps.rs/repo/github/kbknapp/typed-oid/status.svg\n[deps-link]: https://deps.rs/crate/typed-oid\n[rustc-image]: https://img.shields.io/badge/rustc-1.60+-blue.svg\n\n[//]: # (Links)\n\n[LICENSE-MIT]: https://github.com/kbknapp/typed-oid/blob/main/LICENSE-MIT\n[LICENSE-APACHE]: https://github.com/kbknapp/typed-oid/blob/main/LICENSE-APACHE\n[RFC4648]: https://datatracker.ietf.org/doc/html/rfc4648.html#section-7\n[draft RFC4122v14]: https://datatracker.ietf.org/doc/html/draft-ietf-uuidrev-rfc4122bis#name-uuid-version-7\n[seaplane_oid]: https://crates.io/crates/seaplane-oid\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkbknapp%2Ftyped-oid","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkbknapp%2Ftyped-oid","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkbknapp%2Ftyped-oid/lists"}