11 个版本 (4 个破坏性更新)

0.4.2 2024年6月24日
0.4.1 2024年6月24日
0.3.2 2024年4月13日
0.2.1 2024年4月9日
0.0.1 2023年12月28日

#209 in 文本处理

Download history 101/week @ 2024-04-15 162/week @ 2024-06-17 299/week @ 2024-06-24 5/week @ 2024-07-22 106/week @ 2024-07-29

每月111次下载

Apache-2.0 OR MIT

41KB
698

容器镜像参考

Rust Version crates.io Dependency Status

用于使用和处理类型对象 ID 的库。

关于

对象 ID (OID) 是一个基于32进制hex(这是带有扩展十六进制字母表的32进制;有关详细信息,请参阅RFC4648)编码的 UUID。UUID 是 v4(随机)或 v7(基于 UNIX纪元;有关详细信息,请参阅[draft RFC4122v17])- 此库进一步通过一个 "类型" 对 OID 进行了限定,这是一个由一个 - 与 OID 分隔的简短字母数字前缀。此库将类型为 OID 的 TOID 称为 TOID,这些 TOID 与缺少 "类型" 前缀的 OID 对应物不同。

例如 EXA-4GKFGPRVND4QT3PDR90PDKF66O,按照惯例,前缀是三个 ASCII 字符,但这并不是 TOID 的一般性约束。

介绍

TOID 允许使用前缀形式的一个 "人类可读的主题行",其中实际数据是 UUIDv7。这意味着在调试或审查系统时,通过查看前缀即可轻松确定特定位置是否传入了错误的 TOID。这是无法通过裸 UUID 或 GUID 实现的,因为它们缺少任何类型标识符。

换句话说,如果不使用 TOID,则将一个简单的 GUID 的 UserID 与一个 OrderID 混淆的可能性很大。而 TOID 则会使错误更容易识别,甚至程序上不可能发生。

将 UUID 编码为 base32hex 还可以将数据压缩成更小、更易于人类理解的形式,类似于提交哈希。使用 "扩展十六进制编码" 添加了这样一个额外属性,即当按位比较时,编码不会失去它们的排序顺序。

最后,当用作数据库条目时,使用 UUIDv7 可启用索引局部性。

反介绍

TOID 的缺点是在处理 ID 和值时存在一层间接层,TOID 是前缀 UUIDv7 并不是一目了然的。此外,前缀本身必须以某种方式控制,包括在更改时迁移,这为应用层增加了复杂性。

与裸UUID相比,还需要额外的处理开销,以便进行编码/解码以及处理添加和删除前缀。

然而,我们认为这些缺点与从格式中获得的好处相比微不足道。

示例

use typed_oid::{error::*, Oid, OidStr, OidPrefix};
use uuid::Uuid;
use anyhow::Result;

fn main() -> Result<()> {
    // TOIDs come in two flavors, Oid<T> and `OidStr`.

    // A Oid<T> is a TOID using a Rust types as the type, e.g. a true Typed OID
    // These are less ergonomic, but more type-safe.
    run_oid()?;

    // A `OidStr` is a TOID using a bare string as the type, e.g. "Stringly Typed OID"
    // These easier to use, but less type-safe.
    run_oidstr()?;

    Ok(())
}

fn run_oidstr() -> Result<()> {
    // OIDs can be created with a given prefix alone
    #[cfg(feature = "uuid_v4")]
    {
        let oid = OidStr::new_v4("EXA")?;
        println!("OidStr from UUIDv4: {oid}");
    }
    #[cfg(feature = "uuid_v7")]
    {
        let oid = OidStr::new_v7_now("EXA")?;
        println!("OidStr from UUIDv7: {oid}");
    }
    // OIDs can also be created from the raw parts
    let oid = OidStr::try_with_uuid("EXA", "b3cfdafa-3fec-41e2-82bf-ff881131abf1")?;
    println!("OidStr from UUID: {oid}");

    // OIDs can be parsed from strings, however the "value" must be a valid
    // base32hex (no pad) encoded UUID
    let oid: OidStr = "EXA-4GKFGPRVND4QT3PDR90PDKF66O".parse()?;
    println!("OidStr from string: {oid}");

    // One can retrieve the various parts of the OID if needed
    println!("Components of {oid}:");
    println!("\tPrefix: {}", oid.prefix());
    println!("\tValue: {}", oid.value());
    println!("\tUUID: {}", oid.uuid());

    Ok(())
}

fn run_oid() -> Result<()> {
    // In order for a struct to be used as a type it must implement typed_oid::OidPrefix
    #[derive(Debug)]
    struct EXA;
    impl OidPrefix for EXA {}

    // We can create a new OID by generating a random UUID
    #[cfg(feature = "uuid_v4")]
    {
        let oid: Oid<EXA> = Oid::new_v4();
        println!("Oid<EXA> with new UUIDv4: {oid}");
    }
    #[cfg(feature = "uuid_v7")]
    {
        let oid: Oid<EXA> = Oid::new_v7_now();
        println!("Oid<EXA> with new UUIDv7: {oid}");
    }
    // Or by giving a UUID
    let oid: Oid<EXA> = Oid::try_with_uuid("b3cfdafa-3fec-41e2-82bf-ff881131abf1")?;
    println!("Oid<EXA> with new UUID: {oid}");

    // We can go the other direction and parse a string to a Oid<EXA>
    let oid: Oid<EXA> = "EXA-4GKFGPRVND4QT3PDR90PDKF66O".parse()?;
    println!("Oid<EXA> with from string: {oid}");

    // One can retrieve the various parts of the OID if needed
    println!("Components of {oid}:");
    println!("\tPrefix: {}", oid.prefix());
    println!("\tValue: {}", oid.value());
    println!("\tUUID: {}", oid.uuid());

    // However, if we change the prefix to something that doesn't match our EXA type
    // we get an error even if the UUID is valid
    let res = "FAIL-4GKFGPRVND4QT3PDR90PDKF66O".parse::<Oid<EXA>>();
    assert!(res.is_err());
    assert_eq!(res.unwrap_err(), Error::InvalidPrefix { valid_until: 0 });

    Ok(())
}

最低支持的 Rust 版本 (MSRV)

MSRV取决于启用了哪些crate功能

功能 MSRV
uuid_4 1.60.0
uuid_7 1.60.0
serde 1.60.0
surrealdb 1.75.0

许可证

该项目根据您的选择,可在Apache License Version 2.0或MIT许可证下双授权:LICENSE-APACHELICENSE-MIT

typed-oid最初是从seaplane-oid v0.4.0分叉而来,该版本许可协议为Apache License Version 2.0。

依赖项

~2–36MB
~577K SLoC