Vendor dependencies

This commit is contained in:
2026-08-01 16:11:49 +03:00
parent 7f139a0241
commit 6b5e7f0f8b
29706 changed files with 9575646 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
{"$comment":"This file only protects against accidental modifications. It is not a security mechanism and does not protect against malicious changes.","files":{".cargo_vcs_info.json":"905b8eba65b40e35af112c1d0e388f781fe6d9e695b4272f6e01d427765f3dd0",".github/curl_options":"61925f2b08204170a3be3fd11da84ed40fde73ff8a504ab103e6c92c51d03b48",".github/workflows/ci-build.yml":"9123796efbc25997c77692ecbb6b13932da84893766761427f39664ead75603b","Cargo.lock":"f93428a68cd9a96b16d29fafedb6b9fbfc40cf7ec0956edf7d5f8c52167af77e","Cargo.toml":"644c6e583d982285dc9ec0cdd876b6b1bec2f5b5e0481da5fb1d7b68ae4653d2","Cargo.toml.orig":"2f72fac5c6b9593045b11197621e300acadc8d18ef9feead2972a6eaa1675e3a","LICENSE":"d108883b82ded37901ba7679d22d859e278819311f6ab66735df887ed32cc917","README.md":"9741c362737b35a5a9ced30b2f0e3c622e305894b8fd78681c0f860f12cebb70","benches/bench.rs":"bc666d053ec88d769c738e252c0dc88822aa0b69ead3b038a6328ae043b24297","src/base32.rs":"ab736736fc0c1b5c29ad88b3a6f5ccf2ae16bd437f037d43bf4af622882641a5","src/generator.rs":"83ee38f08b78dfd303fcff87648db11d982a4e6400cb6e81cc08b87e81ebe5ac","src/lib.rs":"3cf6fba409ac0b8f04712f4f6c941526fea6301785b11eb3e64713765ca96307","src/postgres.rs":"78f7d096e90cdb3b469de2da3c60fde0f432512d2ea84740cf14a38cc99074f1","src/rkyv.rs":"3ba168d25ad4ca6ebc4ffab784984360ebc374d9a99280260fe0440ece284993","src/serde.rs":"60c017476b06cadb2a8205cd3f7b2f49b2e5a14b2ec81c56a904e06ef0652669","src/time.rs":"8a85278cf16d749fbad257f8d85eb541091a017190995c43ae66ab9bc8e30987","src/time_utils.rs":"76e651f3e0396b4162417dc5be09109d8aee5b193d00553a42da998432105067","src/uuid.rs":"ed08767a0a9530429bdb980bfda96cd4a572c972eec53fd3419705ed5a23883b","tests/wasm32-datetime.rs":"bf862db0499acd02574692adb3b5703157b8bbbdaea55dafb75612713a71d338"},"package":"470dbf6591da1b39d43c14523b2b469c86879a53e8b758c8e090a470fe7b1fbe"}
+6
View File
@@ -0,0 +1,6 @@
{
"git": {
"sha1": "3804e42ab8b7406706364870e407ee002ac184cd"
},
"path_in_vcs": ""
}
+6
View File
@@ -0,0 +1,6 @@
--fail
--location
--retry 3
--retry-connrefused
--show-error
--silent
@@ -0,0 +1,26 @@
name: ci-build
on: [push, pull_request]
jobs:
do-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v4
with:
node-version: 20
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
- run: cargo check --package ulid-cli
- run: cargo test
- run: cargo test --all-features
- run: cargo test --no-default-features --features=std
- run: cargo test --no-default-features
- name: Install wasm-pack
run: cargo install wasm-pack
- run: ~/.cargo/bin/wasm-pack test --node
env:
RUSTFLAGS: '--cfg getrandom_backend="wasm_js"'
+837
View File
@@ -0,0 +1,837 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "base64"
version = "0.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2"
[[package]]
name = "bencher"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dfdb4953a096c551ce9ace855a604d702e6e62d77fac690575ae347571717f5"
[[package]]
name = "bitflags"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "bumpalo"
version = "3.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3"
[[package]]
name = "bytecheck"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50690fb3370fb9fe3550372746084c46f2ac8c9685c583d2be10eefd89d3d1a3"
dependencies = [
"bytecheck_derive",
"ptr_meta",
"rancor",
"simdutf8",
]
[[package]]
name = "bytecheck_derive"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efb7846e0cb180355c2dec69e721edafa36919850f1a9f52ffba4ebc0393cb71"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.98",
]
[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "bytes"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "console_error_panic_hook"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc"
dependencies = [
"cfg-if",
"wasm-bindgen",
]
[[package]]
name = "cpufeatures"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
"subtle",
]
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "fallible-iterator"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
[[package]]
name = "finl_unicode"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6"
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "getrandom"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
dependencies = [
"cfg-if",
"libc",
"wasi 0.10.2+wasi-snapshot-preview1",
]
[[package]]
name = "getrandom"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"wasi 0.13.3+wasi-0.2.2",
"wasm-bindgen",
"windows-targets",
]
[[package]]
name = "hashbrown"
version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
[[package]]
name = "hmac"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
dependencies = [
"digest",
]
[[package]]
name = "indexmap"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "js-sys"
version = "0.3.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
dependencies = [
"once_cell",
"wasm-bindgen",
]
[[package]]
name = "libc"
version = "0.2.169"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
[[package]]
name = "log"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
"cfg-if",
]
[[package]]
name = "md-5"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca"
dependencies = [
"digest",
]
[[package]]
name = "memchr"
version = "2.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c"
[[package]]
name = "munge"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0091202c98cf06da46c279fdf50cccb6b1c43b4521abdf6a27b4c7e71d5d9d7"
dependencies = [
"munge_macro",
]
[[package]]
name = "munge_macro"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "734799cf91479720b2f970c61a22850940dd91e27d4f02b1c6fc792778df2459"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.98",
]
[[package]]
name = "once_cell"
version = "1.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
[[package]]
name = "postgres-protocol"
version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49b6c5ef183cd3ab4ba005f1ca64c21e8bd97ce4699cfea9e8d9a2c4958ca520"
dependencies = [
"base64",
"byteorder",
"bytes",
"fallible-iterator",
"hmac",
"md-5",
"memchr",
"rand 0.8.4",
"sha2",
"stringprep",
]
[[package]]
name = "postgres-types"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d2234cdee9408b523530a9b6d2d6b373d1db34f6a8e51dc03ded1828d7fb67c"
dependencies = [
"bytes",
"fallible-iterator",
"postgres-protocol",
]
[[package]]
name = "ppv-lite86"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]]
name = "proc-macro2"
version = "1.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
dependencies = [
"unicode-ident",
]
[[package]]
name = "ptr_meta"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe9e76f66d3f9606f44e45598d155cb13ecf09f4a28199e48daf8c8fc937ea90"
dependencies = [
"ptr_meta_derive",
]
[[package]]
name = "ptr_meta_derive"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca414edb151b4c8d125c12566ab0d74dc9cdba36fb80eb7b848c15f495fd32d1"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.98",
]
[[package]]
name = "quote"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rancor"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caf5f7161924b9d1cea0e4cabc97c372cea92b5f927fc13c6bca67157a0ad947"
dependencies = [
"ptr_meta",
]
[[package]]
name = "rand"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
dependencies = [
"libc",
"rand_chacha 0.3.1",
"rand_core 0.6.3",
"rand_hc",
]
[[package]]
name = "rand"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
dependencies = [
"rand_chacha 0.9.0",
"rand_core 0.9.0",
"zerocopy",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core 0.6.3",
]
[[package]]
name = "rand_chacha"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core 0.9.0",
]
[[package]]
name = "rand_core"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
dependencies = [
"getrandom 0.2.3",
]
[[package]]
name = "rand_core"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b08f3c9802962f7e1b25113931d94f43ed9725bebc59db9d0c3e9a23b67e15ff"
dependencies = [
"getrandom 0.3.1",
"zerocopy",
]
[[package]]
name = "rand_hc"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
dependencies = [
"rand_core 0.6.3",
]
[[package]]
name = "rend"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a35e8a6bf28cd121053a66aa2e6a2e3eaffad4a60012179f0e864aa5ffeff215"
dependencies = [
"bytecheck",
]
[[package]]
name = "rkyv"
version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e147371c75553e1e2fcdb483944a8540b8438c31426279553b9a8182a9b7b65"
dependencies = [
"bytecheck",
"bytes",
"hashbrown",
"indexmap",
"munge",
"ptr_meta",
"rancor",
"rend",
"rkyv_derive",
"tinyvec",
"uuid",
]
[[package]]
name = "rkyv_derive"
version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "246b40ac189af6c675d124b802e8ef6d5246c53e17367ce9501f8f66a81abb7a"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.98",
]
[[package]]
name = "rustversion"
version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
[[package]]
name = "scoped-tls"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
[[package]]
name = "serde"
version = "1.0.133"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97565067517b60e2d1ea8b268e59ce036de907ac523ad83a0475da04e818989a"
[[package]]
name = "serde_derive"
version = "1.0.133"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed201699328568d8d08208fdd080e3ff594e6c422e438b6705905da01005d537"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.85",
]
[[package]]
name = "sha2"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "simdutf8"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
[[package]]
name = "stringprep"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb41d74e231a107a1b4ee36bd1214b11285b77768d2e3824aedafa988fd36ee6"
dependencies = [
"finl_unicode",
"unicode-bidi",
"unicode-normalization",
]
[[package]]
name = "subtle"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
[[package]]
name = "syn"
version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a684ac3dcd8913827e18cd09a68384ee66c1de24157e3c556c9ab16d85695fb7"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "syn"
version = "2.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tinyvec"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "typenum"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
[[package]]
name = "ulid"
version = "1.2.1"
dependencies = [
"bencher",
"bytes",
"getrandom 0.3.1",
"postgres-types",
"rand 0.9.0",
"rkyv",
"serde",
"serde_derive",
"uuid",
"wasm-bindgen-test",
"web-time",
]
[[package]]
name = "unicode-bidi"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
[[package]]
name = "unicode-ident"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
[[package]]
name = "unicode-normalization"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
dependencies = [
"tinyvec",
]
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "uuid"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]]
name = "wasi"
version = "0.13.3+wasi-0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
dependencies = [
"wit-bindgen-rt",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
dependencies = [
"cfg-if",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
dependencies = [
"bumpalo",
"log",
"proc-macro2",
"quote",
"syn 2.0.98",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de9a9cec1733468a8c657e57fa2413d2ae2c0129b95e87c5b72b8ace4d13f31f"
dependencies = [
"cfg-if",
"js-sys",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.98",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
dependencies = [
"unicode-ident",
]
[[package]]
name = "wasm-bindgen-test"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68b30cf2cba841a812f035c40c50f53eb9c56181192a9dd2c71b65e6a87a05ba"
dependencies = [
"console_error_panic_hook",
"js-sys",
"scoped-tls",
"wasm-bindgen",
"wasm-bindgen-futures",
"wasm-bindgen-test-macro",
]
[[package]]
name = "wasm-bindgen-test-macro"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88ad594bf33e73cafcac2ae9062fc119d4f75f9c77e25022f91c9a64bd5b6463"
dependencies = [
"proc-macro2",
"quote",
]
[[package]]
name = "web-sys"
version = "0.3.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fed94beee57daf8dd7d51f2b15dc2bcde92d7a72304cdf662a4371008b71b90"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "web-time"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ee269d72cc29bf77a2c4bc689cc750fb39f5cbd493d2205bbb3f5c7779cf7b0"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "wit-bindgen-rt"
version = "0.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
dependencies = [
"bitflags",
]
[[package]]
name = "zerocopy"
version = "0.8.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa91407dacce3a68c56de03abe2760159582b846c6a4acd2f456618087f12713"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06718a168365cad3d5ff0bb133aad346959a2074bd4a85c121255a11304a8626"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.98",
]
+97
View File
@@ -0,0 +1,97 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2018"
name = "ulid"
version = "1.2.1"
authors = ["dylanhart <dylan96hart@gmail.com>"]
build = false
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "a Universally Unique Lexicographically Sortable Identifier implementation"
readme = "README.md"
keywords = [
"ulid",
"uuid",
"sortable",
"identifier",
]
license = "MIT"
repository = "https://github.com/dylanhart/ulid-rs"
[package.metadata.docs.rs]
all-features = true
[features]
default = ["std"]
postgres = [
"dep:postgres-types",
"dep:bytes",
]
rkyv = ["dep:rkyv"]
std = ["rand"]
[lib]
name = "ulid"
path = "src/lib.rs"
[[test]]
name = "wasm32-datetime"
path = "tests/wasm32-datetime.rs"
[[bench]]
name = "bench"
path = "benches/bench.rs"
harness = false
[dependencies.bytes]
version = "1.4.0"
optional = true
[dependencies.postgres-types]
version = "0.2.6"
optional = true
[dependencies.rand]
version = "0.9"
optional = true
[dependencies.rkyv]
version = "0.8.10"
optional = true
[dependencies.serde]
version = "1.0"
optional = true
[dependencies.uuid]
version = "1.1"
optional = true
[dev-dependencies.bencher]
version = "0.1"
[dev-dependencies.serde_derive]
version = "1.0"
[target.wasm32-unknown-unknown.dependencies.web-time]
version = "1"
[target.wasm32-unknown-unknown.dev-dependencies.getrandom]
version = "0.3.1"
features = ["wasm_js"]
[target.wasm32-unknown-unknown.dev-dependencies.wasm-bindgen-test]
version = "0.3"
+49
View File
@@ -0,0 +1,49 @@
[package]
name = "ulid"
version = "1.2.1"
authors = ["dylanhart <dylan96hart@gmail.com>"]
edition = "2018"
license = "MIT"
readme = "README.md"
description = "a Universally Unique Lexicographically Sortable Identifier implementation"
keywords = ["ulid", "uuid", "sortable", "identifier"]
repository = "https://github.com/dylanhart/ulid-rs"
[features]
default = ["std"]
std = ["rand"]
postgres = ["dep:postgres-types", "dep:bytes"]
rkyv = ["dep:rkyv"]
[dependencies]
serde = { version = "1.0", optional = true }
rand = { version = "0.9", optional = true }
uuid = { version = "1.1", optional = true }
postgres-types = { version = "0.2.6", optional = true }
bytes = { version = "1.4.0", optional = true }
rkyv = { version = "0.8.10", optional = true }
[target.wasm32-unknown-unknown.dependencies]
web-time = "1"
[dev-dependencies]
bencher = "0.1"
serde_derive = "1.0"
[target.wasm32-unknown-unknown.dev-dependencies]
getrandom = { version = "0.3.1", features = ["wasm_js"] }
wasm-bindgen-test = "0.3"
[[bench]]
name = "bench"
path = "benches/bench.rs"
harness = false
[workspace]
members = ["cli"]
[package.metadata.docs.rs]
all-features = true
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 Dylan Hart
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+51
View File
@@ -0,0 +1,51 @@
# ulid-rs
![Build Status](https://github.com/dylanhart/ulid-rs/actions/workflows/ci-build.yml/badge.svg)
[![Crates.io](https://img.shields.io/crates/v/ulid.svg)](https://crates.io/crates/ulid)
[![docs.rs](https://docs.rs/ulid/badge.svg)](https://docs.rs/ulid)
This is a Rust implementation of the [ulid][ulid] project which provides
Universally Unique Lexicographically Sortable Identifiers.
[ulid]: https://github.com/ulid/spec
## Quickstart
```rust
use ulid::Ulid;
// Generate a ulid
let ulid = Ulid::new();
// Generate a string for a ulid
let s = ulid.to_string();
// Create from a String
let res = Ulid::from_string(&s);
assert_eq!(ulid, res.unwrap());
```
## Crate Features
* **`std` (default)**: Flag to toggle use of `std` and `rand`. Disable this flag for `#[no_std]` support.
* **`serde`**: Enables serialization and deserialization of `Ulid` types via `serde`. ULIDs are serialized using their canonical 26-character representation as defined in the ULID standard. An optional `ulid_as_u128` module is provided, which enables serialization through an `Ulid`'s inner `u128` primitive type. See the [documentation][serde_mod] and [serde docs][serde_docs] for more information.
* **`uuid`**: Implements infallible conversions between ULIDs and UUIDs from the [`uuid`][uuid] crate via the [`std::convert::From`][trait_from] trait.
[serde_mod]: https://docs.rs/ulid/latest/ulid/serde/index.html
[serde_docs]: https://serde.rs/field-attrs.html#with
[uuid]: https://github.com/uuid-rs/uuid
[trait_from]: https://doc.rust-lang.org/std/convert/trait.From.html
## Benchmark
Benchmarks were run on my desktop (Win 10/WSL2 Ubuntu; Ryzen 7 5950x). Run them yourself with `cargo bench`.
```text
test bench_from_string ... bench: 13 ns/iter (+/- 0)
test bench_from_time ... bench: 13 ns/iter (+/- 0)
test bench_generator_generate ... bench: 29 ns/iter (+/- 0)
test bench_new ... bench: 31 ns/iter (+/- 1)
test bench_to_str ... bench: 7 ns/iter (+/- 0)
test bench_to_string ... bench: 19 ns/iter (+/- 0)
```
+47
View File
@@ -0,0 +1,47 @@
use bencher::{benchmark_group, benchmark_main, Bencher};
use std::time::SystemTime;
use ulid::{Generator, Ulid, ULID_LEN};
fn bench_new(b: &mut Bencher) {
b.iter(Ulid::new);
}
fn bench_generator_generate(b: &mut Bencher) {
let mut gen = Generator::new();
b.iter(|| gen.generate().unwrap());
}
fn bench_from_time(b: &mut Bencher) {
let time = SystemTime::now();
b.iter(|| Ulid::from_datetime(time));
}
fn bench_to_str(b: &mut Bencher) {
let ulid = Ulid::new();
b.iter(|| {
let mut buffer = [0; ULID_LEN];
ulid.array_to_str(&mut buffer);
});
}
fn bench_to_string(b: &mut Bencher) {
let ulid = Ulid::new();
b.iter(|| ulid.to_string());
}
fn bench_from_string(b: &mut Bencher) {
let s = Ulid::new().to_string();
b.iter(|| Ulid::from_string(&s).unwrap());
}
benchmark_group!(
ulid_perf,
bench_new,
bench_generator_generate,
bench_from_time,
bench_to_str,
bench_to_string,
bench_from_string
);
benchmark_main!(ulid_perf);
+204
View File
@@ -0,0 +1,204 @@
use core::fmt;
/// Length of a string-encoded Ulid
pub const ULID_LEN: usize = 26;
const ALPHABET: &[u8; 32] = b"0123456789ABCDEFGHJKMNPQRSTVWXYZ";
const NO_VALUE: u8 = 255;
const LOOKUP: [u8; 256] = [
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255,
255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 16, 17, 255, 18, 19, 255, 20, 21, 255, 22, 23, 24,
25, 26, 255, 27, 28, 29, 30, 31, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 16, 17,
255, 18, 19, 255, 20, 21, 255, 22, 23, 24, 25, 26, 255, 27, 28, 29, 30, 31, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
];
/// Generator code for `LOOKUP`
#[cfg(test)]
#[test]
fn test_lookup_table() {
let mut lookup = [NO_VALUE; 256];
for (i, &c) in ALPHABET.iter().enumerate() {
lookup[c as usize] = i as u8;
if !(c as char).is_numeric() {
//lowercase
lookup[(c + 32) as usize] = i as u8;
}
}
assert_eq!(LOOKUP, lookup);
}
/// An error that can occur when encoding a base32 string
#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
pub enum EncodeError {
/// The length of the provided buffer is not large enough
BufferTooSmall,
}
#[cfg(feature = "std")]
impl std::error::Error for EncodeError {}
impl fmt::Display for EncodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
let text = match *self {
EncodeError::BufferTooSmall => "buffer too small",
};
write!(f, "{}", text)
}
}
/// Encode a u128 value to a given buffer. The provided buffer should be at least `ULID_LEN` long.
#[deprecated(
since = "1.2.0",
note = "Use the infallible `encode_to_array` instead."
)]
pub fn encode_to(mut value: u128, buffer: &mut [u8]) -> Result<usize, EncodeError> {
// NOTE: This function can't be made const because mut refs aren't allowed for some reason
if buffer.len() < ULID_LEN {
return Err(EncodeError::BufferTooSmall);
}
for i in 0..ULID_LEN {
buffer[ULID_LEN - 1 - i] = ALPHABET[(value & 0x1f) as usize];
value >>= 5;
}
Ok(ULID_LEN)
}
/// Encode a u128 value to a given buffer.
pub fn encode_to_array(mut value: u128, buffer: &mut [u8; ULID_LEN]) {
// NOTE: This function can't be made const because mut refs aren't allowed for some reason
for i in 0..ULID_LEN {
buffer[ULID_LEN - 1 - i] = ALPHABET[(value & 0x1f) as usize];
value >>= 5;
}
}
#[cfg(feature = "std")]
pub fn encode(value: u128) -> String {
let mut buffer: [u8; ULID_LEN] = [0; ULID_LEN];
encode_to_array(value, &mut buffer);
String::from_utf8(buffer.to_vec()).expect("unexpected failure in base32 encode for ulid")
}
/// An error that can occur when decoding a base32 string
#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
pub enum DecodeError {
/// The length of the string does not match the expected length
InvalidLength,
/// A non-base32 character was found
InvalidChar,
}
#[cfg(feature = "std")]
impl std::error::Error for DecodeError {}
impl fmt::Display for DecodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
let text = match *self {
DecodeError::InvalidLength => "invalid length",
DecodeError::InvalidChar => "invalid character",
};
write!(f, "{}", text)
}
}
pub const fn decode(encoded: &str) -> Result<u128, DecodeError> {
if encoded.len() != ULID_LEN {
return Err(DecodeError::InvalidLength);
}
let mut value: u128 = 0;
let bytes = encoded.as_bytes();
// Manual for loop because Range::iter() isn't const
let mut i = 0;
while i < ULID_LEN {
let val = LOOKUP[bytes[i] as usize];
if val != NO_VALUE {
value = (value << 5) | val as u128;
} else {
return Err(DecodeError::InvalidChar);
}
i += 1;
}
Ok(value)
}
#[cfg(all(test, feature = "std"))]
mod tests {
use super::*;
#[test]
fn test_valid() {
let val = 0x41414141414141414141414141414141;
assert_eq!(decode("21850M2GA1850M2GA1850M2GA1").unwrap(), val);
assert_eq!(encode(val), "21850M2GA1850M2GA1850M2GA1");
let val = 0x4d4e385051444a59454234335a413756;
let enc = "2D9RW50MA499CMAGHM6DD42DTP";
let lower = enc.to_lowercase();
assert_eq!(encode(val), enc);
assert_eq!(decode(enc).unwrap(), val);
assert_eq!(decode(&lower).unwrap(), val);
}
#[test]
fn test_length() {
assert_eq!(encode(0xffffffffffffffffffffffffffffffff).len(), ULID_LEN);
assert_eq!(encode(0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f).len(), ULID_LEN);
assert_eq!(encode(0x00000000000000000000000000000000).len(), ULID_LEN);
assert_eq!(decode(""), Err(DecodeError::InvalidLength));
assert_eq!(
decode("2D9RW50MA499CMAGHM6DD42DT"),
Err(DecodeError::InvalidLength)
);
assert_eq!(
decode("2D9RW50MA499CMAGHM6DD42DTPP"),
Err(DecodeError::InvalidLength)
);
}
#[test]
fn test_chars() {
for ref c in encode(0xffffffffffffffffffffffffffffffff).bytes() {
assert!(ALPHABET.contains(c));
}
for ref c in encode(0x0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f).bytes() {
assert!(ALPHABET.contains(c));
}
for ref c in encode(0x00000000000000000000000000000000).bytes() {
assert!(ALPHABET.contains(c));
}
assert_eq!(
decode("2D9RW50[A499CMAGHM6DD42DTP"),
Err(DecodeError::InvalidChar)
);
assert_eq!(
decode("2D9RW50LA499CMAGHM6DD42DTP"),
Err(DecodeError::InvalidChar)
);
assert_eq!(
decode("2D9RW50IA499CMAGHM6DD42DTP"),
Err(DecodeError::InvalidChar)
);
}
}
+204
View File
@@ -0,0 +1,204 @@
use std::time::{Duration, SystemTime};
use std::fmt;
use crate::Ulid;
/// A Ulid generator that provides monotonically increasing Ulids
pub struct Generator {
previous: Ulid,
}
impl Generator {
/// Create a new ulid generator for monotonically ordered ulids
///
/// # Example
/// ```rust
/// use ulid::Generator;
///
/// let mut gen = Generator::new();
///
/// let ulid1 = gen.generate().unwrap();
/// let ulid2 = gen.generate().unwrap();
///
/// assert!(ulid1 < ulid2);
/// ```
pub const fn new() -> Generator {
Generator {
previous: Ulid::nil(),
}
}
/// Generate a new Ulid. Each call is guaranteed to provide a Ulid with a larger value than the
/// last call. If the random bits would overflow, this method will return an error.
///
/// ```rust
/// use ulid::Generator;
/// let mut gen = Generator::new();
///
/// let ulid1 = gen.generate().unwrap();
/// let ulid2 = gen.generate().unwrap();
///
/// assert!(ulid1 < ulid2);
/// ```
pub fn generate(&mut self) -> Result<Ulid, MonotonicError> {
self.generate_from_datetime(crate::time_utils::now())
}
/// Generate a new Ulid matching the given DateTime.
/// Each call is guaranteed to provide a Ulid with a larger value than the last call.
/// If the random bits would overflow, this method will return an error.
///
/// # Example
/// ```rust
/// use ulid::Generator;
/// use std::time::SystemTime;
///
/// let dt = SystemTime::now();
/// let mut gen = Generator::new();
///
/// let ulid1 = gen.generate_from_datetime(dt).unwrap();
/// let ulid2 = gen.generate_from_datetime(dt).unwrap();
///
/// assert_eq!(ulid1.datetime(), ulid2.datetime());
/// assert!(ulid1 < ulid2);
/// ```
pub fn generate_from_datetime(&mut self, datetime: SystemTime) -> Result<Ulid, MonotonicError> {
self.generate_from_datetime_with_source(datetime, &mut rand::rng())
}
/// Generate a new monotonic increasing Ulid with the given source
/// Each call is guaranteed to provide a Ulid with a larger value than the last call.
/// If the random bits would overflow, this method will return an error.
///
/// # Example
/// ```rust
/// use ulid::Generator;
/// use ulid::Ulid;
/// use std::time::SystemTime;
/// use rand::prelude::*;
///
/// let mut rng = StdRng::from_os_rng();
/// let mut gen = Generator::new();
///
/// let ulid1 = gen.generate_with_source(&mut rng).unwrap();
/// let ulid2 = gen.generate_with_source(&mut rng).unwrap();
///
/// assert!(ulid1 < ulid2);
/// ```
pub fn generate_with_source<R>(&mut self, source: &mut R) -> Result<Ulid, MonotonicError>
where
R: rand::Rng + ?Sized,
{
self.generate_from_datetime_with_source(crate::time_utils::now(), source)
}
/// Generate a new monotonic increasing Ulid with the given source matching the given DateTime
/// Each call is guaranteed to provide a Ulid with a larger value than the last call.
/// If the random bits would overflow, this method will return an error.
///
/// # Example
/// ```rust
/// use ulid::Generator;
/// use std::time::SystemTime;
/// use rand::prelude::*;
///
/// let dt = SystemTime::now();
/// let mut rng = StdRng::from_os_rng();
/// let mut gen = Generator::new();
///
/// let ulid1 = gen.generate_from_datetime_with_source(dt, &mut rng).unwrap();
/// let ulid2 = gen.generate_from_datetime_with_source(dt, &mut rng).unwrap();
///
/// assert_eq!(ulid1.datetime(), ulid2.datetime());
/// assert!(ulid1 < ulid2);
/// ```
pub fn generate_from_datetime_with_source<R>(
&mut self,
datetime: SystemTime,
source: &mut R,
) -> Result<Ulid, MonotonicError>
where
R: rand::Rng + ?Sized,
{
let last_ms = self.previous.timestamp_ms();
// maybe time went backward, or it is the same ms.
// increment instead of generating a new random so that it is monotonic
if datetime
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap_or(Duration::ZERO)
.as_millis()
<= u128::from(last_ms)
{
if let Some(next) = self.previous.increment() {
self.previous = next;
return Ok(next);
} else {
return Err(MonotonicError::Overflow);
}
}
let next = Ulid::from_datetime_with_source(datetime, source);
self.previous = next;
Ok(next)
}
}
impl Default for Generator {
fn default() -> Self {
Self::new()
}
}
/// Error while trying to generate a monotonic increment in the same millisecond
#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
pub enum MonotonicError {
/// Would overflow into the next millisecond
Overflow,
}
impl std::error::Error for MonotonicError {}
impl fmt::Display for MonotonicError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
let text = match *self {
MonotonicError::Overflow => "Ulid random bits would overflow",
};
write!(f, "{}", text)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::Duration;
#[test]
fn test_order_monotonic() {
let dt = SystemTime::now();
let mut gen = Generator::new();
let ulid1 = gen.generate_from_datetime(dt).unwrap();
let ulid2 = gen.generate_from_datetime(dt).unwrap();
let ulid3 = Ulid::from_datetime(dt + Duration::from_millis(1));
assert_eq!(ulid1.0 + 1, ulid2.0);
assert!(ulid2 < ulid3);
assert!(ulid2.timestamp_ms() < ulid3.timestamp_ms())
}
#[test]
fn test_order_monotonic_with_source() {
use rand::rngs::mock::StepRng;
let mut source = StepRng::new(123, 0);
let mut gen = Generator::new();
let _has_default = Generator::default();
let ulid1 = gen.generate_with_source(&mut source).unwrap();
let ulid2 = gen.generate_with_source(&mut source).unwrap();
assert!(ulid1 < ulid2);
}
#[test]
fn can_display_things() {
println!("{}", MonotonicError::Overflow);
}
}
+439
View File
@@ -0,0 +1,439 @@
#![warn(missing_docs)]
//! # ulid-rs
//!
//! This is a Rust implementation of the [ulid][ulid] project which provides
//! Universally Unique Lexicographically Sortable Identifiers.
//!
//! [ulid]: https://github.com/ulid/spec
//!
//!
//! ## Quickstart
//!
//! ```rust
//! # use ulid::Ulid;
//! // Generate a ulid
//! # #[cfg(not(feature = "std"))]
//! # let ulid = Ulid::default();
//! # #[cfg(feature = "std")]
//! let ulid = Ulid::new();
//!
//! // Generate a string for a ulid
//! let s = ulid.to_string();
//!
//! // Create from a String
//! let res = Ulid::from_string(&s);
//! assert_eq!(ulid, res.unwrap());
//!
//! // Or using FromStr
//! let res = s.parse();
//! assert_eq!(ulid, res.unwrap());
//!
//! ```
#![cfg_attr(not(feature = "std"), no_std)]
#[doc = include_str!("../README.md")]
#[cfg(all(doctest, feature = "std"))]
struct ReadMeDoctest;
mod base32;
#[cfg(feature = "std")]
mod generator;
#[cfg(feature = "postgres")]
mod postgres;
#[cfg(feature = "rkyv")]
mod rkyv;
#[cfg(feature = "serde")]
pub mod serde;
#[cfg(feature = "std")]
mod time;
#[cfg(feature = "std")]
mod time_utils;
#[cfg(feature = "uuid")]
mod uuid;
use core::convert::TryFrom;
use core::fmt;
use core::str::FromStr;
pub use crate::base32::{DecodeError, EncodeError, ULID_LEN};
#[cfg(feature = "std")]
pub use crate::generator::{Generator, MonotonicError};
/// Create a right-aligned bitmask of $len bits
macro_rules! bitmask {
($len:expr) => {
((1 << $len) - 1)
};
}
// Allow other modules to use the macro
pub(crate) use bitmask;
/// A Ulid is a unique 128-bit lexicographically sortable identifier
///
/// Canonically, it is represented as a 26 character Crockford Base32 encoded
/// string.
///
/// Of the 128-bits, the first 48 are a unix timestamp in milliseconds. The
/// remaining 80 are random. The first 48 provide for lexicographic sorting and
/// the remaining 80 ensure that the identifier is unique.
#[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Hash, Clone, Copy)]
#[cfg_attr(
feature = "rkyv",
derive(::rkyv::Archive, ::rkyv::Serialize, ::rkyv::Deserialize)
)]
pub struct Ulid(pub u128);
impl Ulid {
/// The number of bits in a Ulid's time portion
pub const TIME_BITS: u8 = 48;
/// The number of bits in a Ulid's random portion
pub const RAND_BITS: u8 = 80;
/// Create a Ulid from separated parts.
///
/// NOTE: Any overflow bits in the given args are discarded
///
/// # Example
/// ```rust
/// use ulid::Ulid;
///
/// let ulid = Ulid::from_string("01D39ZY06FGSCTVN4T2V9PKHFZ").unwrap();
///
/// let ulid2 = Ulid::from_parts(ulid.timestamp_ms(), ulid.random());
///
/// assert_eq!(ulid, ulid2);
/// ```
pub const fn from_parts(timestamp_ms: u64, random: u128) -> Ulid {
let time_part = (timestamp_ms & bitmask!(Self::TIME_BITS)) as u128;
let rand_part = random & bitmask!(Self::RAND_BITS);
Ulid((time_part << Self::RAND_BITS) | rand_part)
}
/// Creates a Ulid from a Crockford Base32 encoded string
///
/// An DecodeError will be returned when the given string is not formatted
/// properly.
///
/// # Example
/// ```rust
/// use ulid::Ulid;
///
/// let text = "01D39ZY06FGSCTVN4T2V9PKHFZ";
/// let result = Ulid::from_string(text);
///
/// assert!(result.is_ok());
/// assert_eq!(&result.unwrap().to_string(), text);
/// ```
pub const fn from_string(encoded: &str) -> Result<Ulid, DecodeError> {
match base32::decode(encoded) {
Ok(int_val) => Ok(Ulid(int_val)),
Err(err) => Err(err),
}
}
/// The 'nil Ulid'.
///
/// The nil Ulid is special form of Ulid that is specified to have
/// all 128 bits set to zero.
///
/// # Example
/// ```rust
/// use ulid::Ulid;
///
/// let ulid = Ulid::nil();
///
/// assert_eq!(
/// ulid.to_string(),
/// "00000000000000000000000000"
/// );
/// ```
pub const fn nil() -> Ulid {
Ulid(0)
}
/// Gets the timestamp section of this ulid
///
/// # Example
/// ```rust
/// # #[cfg(feature = "std")] {
/// use std::time::{SystemTime, Duration};
/// use ulid::Ulid;
///
/// let dt = SystemTime::now();
/// let ulid = Ulid::from_datetime(dt);
///
/// assert_eq!(u128::from(ulid.timestamp_ms()), dt.duration_since(SystemTime::UNIX_EPOCH).unwrap_or(Duration::ZERO).as_millis());
/// # }
/// ```
pub const fn timestamp_ms(&self) -> u64 {
(self.0 >> Self::RAND_BITS) as u64
}
/// Gets the random section of this ulid
///
/// # Example
/// ```rust
/// use ulid::Ulid;
///
/// let text = "01D39ZY06FGSCTVN4T2V9PKHFZ";
/// let ulid = Ulid::from_string(text).unwrap();
/// let ulid_next = ulid.increment().unwrap();
///
/// assert_eq!(ulid.random() + 1, ulid_next.random());
/// ```
pub const fn random(&self) -> u128 {
self.0 & bitmask!(Self::RAND_BITS)
}
/// Creates a Crockford Base32 encoded string that represents this Ulid
///
/// # Example
/// ```rust
/// use ulid::Ulid;
///
/// let text = "01D39ZY06FGSCTVN4T2V9PKHFZ";
/// let ulid = Ulid::from_string(text).unwrap();
///
/// let mut buf = [0; ulid::ULID_LEN];
/// let new_text = ulid.to_str(&mut buf).unwrap();
///
/// assert_eq!(new_text, text);
/// ```
#[deprecated(since = "1.2.0", note = "Use the infallible `array_to_str` instead.")]
pub fn to_str<'buf>(&self, buf: &'buf mut [u8]) -> Result<&'buf mut str, EncodeError> {
#[allow(deprecated)]
let len = base32::encode_to(self.0, buf)?;
Ok(unsafe { core::str::from_utf8_unchecked_mut(&mut buf[..len]) })
}
/// Creates a Crockford Base32 encoded string that represents this Ulid
///
/// # Example
/// ```rust
/// use ulid::Ulid;
///
/// let text = "01D39ZY06FGSCTVN4T2V9PKHFZ";
/// let ulid = Ulid::from_string(text).unwrap();
///
/// let mut buf = [0; ulid::ULID_LEN];
/// let new_text = ulid.array_to_str(&mut buf);
///
/// assert_eq!(new_text, text);
/// ```
pub fn array_to_str<'buf>(&self, buf: &'buf mut [u8; ULID_LEN]) -> &'buf mut str {
base32::encode_to_array(self.0, buf);
unsafe { core::str::from_utf8_unchecked_mut(buf) }
}
/// Creates a Crockford Base32 encoded string that represents this Ulid
///
/// # Example
/// ```rust
/// use ulid::Ulid;
///
/// let text = "01D39ZY06FGSCTVN4T2V9PKHFZ";
/// let ulid = Ulid::from_string(text).unwrap();
///
/// assert_eq!(&ulid.to_string(), text);
/// ```
#[allow(clippy::inherent_to_string_shadow_display)] // Significantly faster than Display::to_string
#[cfg(feature = "std")]
pub fn to_string(&self) -> String {
base32::encode(self.0)
}
/// Test if the Ulid is nil
///
/// # Example
/// ```rust
/// use ulid::Ulid;
///
/// # #[cfg(not(feature = "std"))]
/// # let ulid = Ulid(1);
/// # #[cfg(feature = "std")]
/// let ulid = Ulid::new();
/// assert!(!ulid.is_nil());
///
/// let nil = Ulid::nil();
/// assert!(nil.is_nil());
/// ```
pub const fn is_nil(&self) -> bool {
self.0 == 0u128
}
/// Increment the random number, make sure that the ts millis stays the same
pub const fn increment(&self) -> Option<Ulid> {
const MAX_RANDOM: u128 = bitmask!(Ulid::RAND_BITS);
if (self.0 & MAX_RANDOM) == MAX_RANDOM {
None
} else {
Some(Ulid(self.0 + 1))
}
}
/// Creates a Ulid using the provided bytes array.
///
/// # Example
/// ```
/// use ulid::Ulid;
/// let bytes = [0xFF; 16];
///
/// let ulid = Ulid::from_bytes(bytes);
///
/// assert_eq!(
/// ulid.to_string(),
/// "7ZZZZZZZZZZZZZZZZZZZZZZZZZ"
/// );
/// ```
pub const fn from_bytes(bytes: [u8; 16]) -> Ulid {
Self(u128::from_be_bytes(bytes))
}
/// Returns the bytes of the Ulid in big-endian order.
///
/// # Example
/// ```
/// use ulid::Ulid;
///
/// let text = "7ZZZZZZZZZZZZZZZZZZZZZZZZZ";
/// let ulid = Ulid::from_string(text).unwrap();
///
/// assert_eq!(ulid.to_bytes(), [0xFF; 16]);
/// ```
pub const fn to_bytes(&self) -> [u8; 16] {
self.0.to_be_bytes()
}
}
impl Default for Ulid {
fn default() -> Self {
Ulid::nil()
}
}
#[cfg(feature = "std")]
impl From<Ulid> for String {
fn from(ulid: Ulid) -> String {
ulid.to_string()
}
}
impl From<(u64, u64)> for Ulid {
fn from((msb, lsb): (u64, u64)) -> Self {
Ulid(u128::from(msb) << 64 | u128::from(lsb))
}
}
impl From<Ulid> for (u64, u64) {
fn from(ulid: Ulid) -> (u64, u64) {
((ulid.0 >> 64) as u64, (ulid.0 & bitmask!(64)) as u64)
}
}
impl From<u128> for Ulid {
fn from(value: u128) -> Ulid {
Ulid(value)
}
}
impl From<Ulid> for u128 {
fn from(ulid: Ulid) -> u128 {
ulid.0
}
}
impl From<[u8; 16]> for Ulid {
fn from(bytes: [u8; 16]) -> Self {
Self(u128::from_be_bytes(bytes))
}
}
impl From<Ulid> for [u8; 16] {
fn from(ulid: Ulid) -> Self {
ulid.0.to_be_bytes()
}
}
impl FromStr for Ulid {
type Err = DecodeError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ulid::from_string(s)
}
}
impl TryFrom<&'_ str> for Ulid {
type Error = DecodeError;
fn try_from(value: &'_ str) -> Result<Self, Self::Error> {
Ulid::from_string(value)
}
}
impl fmt::Display for Ulid {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
let mut buffer = [0; ULID_LEN];
write!(f, "{}", self.array_to_str(&mut buffer))
}
}
#[cfg(all(test, feature = "std"))]
mod tests {
use super::*;
#[test]
fn test_static() {
let s = Ulid(0x41414141414141414141414141414141).to_string();
let u = Ulid::from_string(&s).unwrap();
assert_eq!(&s, "21850M2GA1850M2GA1850M2GA1");
assert_eq!(u.0, 0x41414141414141414141414141414141);
}
#[test]
fn test_increment() {
let ulid = Ulid::from_string("01BX5ZZKBKAZZZZZZZZZZZZZZZ").unwrap();
let ulid = ulid.increment().unwrap();
assert_eq!("01BX5ZZKBKB000000000000000", ulid.to_string());
let ulid = Ulid::from_string("01BX5ZZKBKZZZZZZZZZZZZZZZX").unwrap();
let ulid = ulid.increment().unwrap();
assert_eq!("01BX5ZZKBKZZZZZZZZZZZZZZZY", ulid.to_string());
let ulid = ulid.increment().unwrap();
assert_eq!("01BX5ZZKBKZZZZZZZZZZZZZZZZ", ulid.to_string());
assert!(ulid.increment().is_none());
}
#[test]
fn test_increment_overflow() {
let ulid = Ulid(u128::max_value());
assert!(ulid.increment().is_none());
}
#[test]
fn can_into_thing() {
let ulid = Ulid::from_str("01FKMG6GAG0PJANMWFN84TNXCD").unwrap();
let s: String = ulid.into();
let u: u128 = ulid.into();
let uu: (u64, u64) = ulid.into();
let bytes: [u8; 16] = ulid.into();
assert_eq!(Ulid::from_str(&s).unwrap(), ulid);
assert_eq!(Ulid::from(u), ulid);
assert_eq!(Ulid::from(uu), ulid);
assert_eq!(Ulid::from(bytes), ulid);
}
#[test]
fn default_is_nil() {
assert_eq!(Ulid::default(), Ulid::nil());
}
#[test]
fn can_display_things() {
println!("{}", Ulid::nil());
println!("{}", EncodeError::BufferTooSmall);
println!("{}", DecodeError::InvalidLength);
println!("{}", DecodeError::InvalidChar);
}
}
+54
View File
@@ -0,0 +1,54 @@
use crate::Ulid;
use bytes::BufMut;
use bytes::BytesMut;
use postgres_types::accepts;
use postgres_types::to_sql_checked;
use postgres_types::{FromSql, IsNull, ToSql, Type};
use std::error::Error;
use std::u128;
impl FromSql<'_> for Ulid {
fn from_sql(_ty: &Type, raw: &[u8]) -> Result<Self, Box<dyn Error + Sync + Send>> {
if raw.len() != 16 {
return Err("invalid message length: uuid size mismatch".into());
}
let mut bytes = [0; 16];
bytes.copy_from_slice(raw);
Ok(Ulid(u128::from_be_bytes(bytes)))
}
accepts!(UUID);
}
impl ToSql for Ulid {
fn to_sql(&self, _: &Type, w: &mut BytesMut) -> Result<IsNull, Box<dyn Error + Sync + Send>> {
let bytes: u128 = self.0.into();
w.put_slice(&bytes.to_be_bytes());
Ok(IsNull::No)
}
accepts!(UUID);
to_sql_checked!();
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Ulid;
use postgres_types::{FromSql, Type};
use std::io::Read;
#[test]
fn postgres_cycle() {
let ulid = Ulid::from_string("3Q38XWW0Q98GMAD3NHWZM2PZWZ").unwrap();
let mut w = bytes::BytesMut::new();
let t = &Type::UUID;
let _ = ulid.to_sql(t, &mut w);
assert_eq!(16, w.len());
let bs = w.bytes().map(|v| v.unwrap()).collect::<Vec<u8>>();
assert_eq!(ulid, Ulid::from_sql(t, &bs).unwrap());
}
}
+22
View File
@@ -0,0 +1,22 @@
#[cfg(test)]
mod tests {
use crate::Ulid;
use rkyv::{{from_bytes, to_bytes}, rancor::Error};
#[test]
fn test_ulid_roundtrip() {
// Create a ULID
let id = Ulid::new();
let id_string = id.to_string();
// Serialize it
let bytes = to_bytes::<Error>(&id).unwrap();
// Deserialize back
let deserialized: Ulid = from_bytes::<_, Error>(&bytes).unwrap();
// Verify equality
assert_eq!(id, deserialized);
assert_eq!(id_string, deserialized.to_string());
}
}
+115
View File
@@ -0,0 +1,115 @@
//! Serialization and deserialization.
//!
//! By default, serialization and deserialization go through ULID's 26-character
//! canonical string representation as set by the ULID standard.
//!
//! ULIDs can optionally be serialized as u128 integers using the `ulid_as_u128`
//! module. See the module's documentation for examples.
use crate::{Ulid, ULID_LEN};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
impl Serialize for Ulid {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut buffer = [0; ULID_LEN];
let text = self.array_to_str(&mut buffer);
text.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for Ulid {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let deserialized_str = String::deserialize(deserializer)?;
Self::from_string(&deserialized_str).map_err(serde::de::Error::custom)
}
}
/// Serialization and deserialization of ULIDs through their inner u128 type.
///
/// To use it, annotate a field with
/// `#[serde(with = "ulid_as_u128")]`,
/// `#[serde(serialize_with = "ulid_as_u128")]`, or
/// `#[serde(deserialize_with = "ulid_as_u128")]`.
///
/// # Examples
/// ```
/// # use ulid::Ulid;
/// # use ulid::serde::ulid_as_u128;
/// # use serde_derive::{Serialize, Deserialize};
/// #[derive(Serialize, Deserialize)]
/// struct U128Example {
/// #[serde(with = "ulid_as_u128")]
/// identifier: Ulid
/// }
/// ```
pub mod ulid_as_u128 {
use crate::Ulid;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
/// Serializes a ULID as a u128 type.
pub fn serialize<S>(value: &Ulid, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
value.0.serialize(serializer)
}
/// Deserializes a ULID from a u128 type.
pub fn deserialize<'de, D>(deserializer: D) -> Result<Ulid, D::Error>
where
D: Deserializer<'de>,
{
let deserialized_u128 = u128::deserialize(deserializer)?;
Ok(Ulid(deserialized_u128))
}
}
/// Serialization and deserialization of ULIDs through UUID strings.
///
/// To use this module, annotate a field with
/// `#[serde(with = "ulid_as_uuid")]`,
/// `#[serde(serialize_with = "ulid_as_uuid")]`, or
/// `#[serde(deserialize_with = "ulid_as_uuid")]`.
///
/// # Examples
/// ```
/// # use ulid::Ulid;
/// # use ulid::serde::ulid_as_uuid;
/// # use serde_derive::{Serialize, Deserialize};
/// #[derive(Serialize, Deserialize)]
/// struct UuidExample {
/// #[serde(with = "ulid_as_uuid")]
/// identifier: Ulid
/// }
/// ```
#[cfg(all(feature = "uuid", feature = "serde"))]
pub mod ulid_as_uuid {
use crate::Ulid;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use uuid::Uuid;
/// Converts the ULID to a UUID and serializes it as a string.
pub fn serialize<S>(value: &Ulid, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let uuid: Uuid = (*value).into();
uuid.to_string().serialize(serializer)
}
/// Deserializes a ULID from a string containing a UUID.
pub fn deserialize<'de, D>(deserializer: D) -> Result<Ulid, D::Error>
where
D: Deserializer<'de>,
{
let de_string = String::deserialize(deserializer)?;
let de_uuid = Uuid::parse_str(&de_string).map_err(serde::de::Error::custom)?;
Ok(Ulid::from(de_uuid))
}
}
+183
View File
@@ -0,0 +1,183 @@
use crate::{bitmask, Ulid};
use std::time::{Duration, SystemTime};
impl Ulid {
/// Creates a new Ulid with the current time (UTC)
///
/// Using this function to generate Ulids will not guarantee monotonic sort order.
/// See [ulid::Generator] for a monotonic sort order.
/// # Example
/// ```rust
/// use ulid::Ulid;
///
/// let my_ulid = Ulid::new();
/// ```
pub fn new() -> Ulid {
Ulid::from_datetime(crate::time_utils::now())
}
/// Creates a new Ulid using data from the given random number generator
///
/// # Example
/// ```rust
/// use rand::prelude::*;
/// use ulid::Ulid;
///
/// let mut rng = StdRng::from_os_rng();
/// let ulid = Ulid::with_source(&mut rng);
/// ```
pub fn with_source<R: rand::Rng>(source: &mut R) -> Ulid {
Ulid::from_datetime_with_source(crate::time_utils::now(), source)
}
/// Creates a new Ulid with the given datetime
///
/// This can be useful when migrating data to use Ulid identifiers.
///
/// This will take the maximum of the `[SystemTime]` argument and `[SystemTime::UNIX_EPOCH]`
/// as earlier times are not valid for a Ulid timestamp
///
/// # Example
/// ```rust
/// use std::time::{SystemTime, Duration};
/// use ulid::Ulid;
///
/// let ulid = Ulid::from_datetime(SystemTime::now());
/// ```
pub fn from_datetime(datetime: SystemTime) -> Ulid {
Ulid::from_datetime_with_source(datetime, &mut rand::rng())
}
/// Creates a new Ulid with the given datetime and random number generator
///
/// This will take the maximum of the `[SystemTime]` argument and `[SystemTime::UNIX_EPOCH]`
/// as earlier times are not valid for a Ulid timestamp
///
/// # Example
/// ```rust
/// use std::time::{SystemTime, Duration};
/// use rand::prelude::*;
/// use ulid::Ulid;
///
/// let mut rng = StdRng::from_os_rng();
/// let ulid = Ulid::from_datetime_with_source(SystemTime::now(), &mut rng);
/// ```
pub fn from_datetime_with_source<R>(datetime: SystemTime, source: &mut R) -> Ulid
where
R: rand::Rng + ?Sized,
{
let timestamp = datetime
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap_or(Duration::ZERO)
.as_millis();
let timebits = (timestamp & bitmask!(Self::TIME_BITS)) as u64;
let msb = timebits << 16 | u64::from(source.random::<u16>());
let lsb = source.random::<u64>();
Ulid::from((msb, lsb))
}
/// Gets the datetime of when this Ulid was created accurate to 1ms
///
/// # Example
/// ```rust
/// use std::time::{SystemTime, Duration};
/// use ulid::Ulid;
///
/// let dt = SystemTime::now();
/// let ulid = Ulid::from_datetime(dt);
///
/// assert!(
/// dt + Duration::from_millis(1) >= ulid.datetime()
/// && dt - Duration::from_millis(1) <= ulid.datetime()
/// );
/// ```
pub fn datetime(&self) -> SystemTime {
let stamp = self.timestamp_ms();
SystemTime::UNIX_EPOCH + Duration::from_millis(stamp)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dynamic() {
let ulid = Ulid::new();
let encoded = ulid.to_string();
let ulid2 = Ulid::from_string(&encoded).expect("failed to deserialize");
println!("{}", encoded);
println!("{:?}", ulid);
println!("{:?}", ulid2);
assert_eq!(ulid, ulid2);
}
#[test]
fn test_source() {
use rand::rngs::mock::StepRng;
let mut source = StepRng::new(123, 0);
let u1 = Ulid::with_source(&mut source);
let dt = SystemTime::now() + Duration::from_millis(1);
let u2 = Ulid::from_datetime_with_source(dt, &mut source);
let u3 = Ulid::from_datetime_with_source(dt, &mut source);
assert!(u1 < u2);
assert_eq!(u2, u3);
}
#[test]
fn test_order() {
let dt = SystemTime::now();
let ulid1 = Ulid::from_datetime(dt);
let ulid2 = Ulid::from_datetime(dt + Duration::from_millis(1));
assert!(ulid1 < ulid2);
}
#[test]
fn test_datetime() {
let dt = SystemTime::now();
let ulid = Ulid::from_datetime(dt);
println!("{:?}, {:?}", dt, ulid.datetime());
assert!(ulid.datetime() <= dt);
assert!(ulid.datetime() + Duration::from_millis(1) >= dt);
}
#[test]
fn test_timestamp() {
let dt = SystemTime::now();
let ulid = Ulid::from_datetime(dt);
let ts = dt
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_millis();
assert_eq!(u128::from(ulid.timestamp_ms()), ts);
}
#[test]
fn default_is_nil() {
assert_eq!(Ulid::default(), Ulid::nil());
}
#[test]
fn nil_is_at_unix_epoch() {
assert_eq!(Ulid::nil().datetime(), SystemTime::UNIX_EPOCH);
}
#[test]
fn truncates_at_unix_epoch() {
if let Some(before_epoch) = SystemTime::UNIX_EPOCH.checked_sub(Duration::from_secs(100)) {
assert!(before_epoch < SystemTime::UNIX_EPOCH);
assert_eq!(
Ulid::from_datetime(before_epoch).datetime(),
SystemTime::UNIX_EPOCH
);
} else {
// Prior dates are not representable (e.g. wasm32-wasi)
}
}
}
+9
View File
@@ -0,0 +1,9 @@
pub(crate) fn now() -> std::time::SystemTime {
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
{
use web_time::web::SystemTimeExt;
return web_time::SystemTime::now().to_std();
}
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
return std::time::SystemTime::now();
}
+42
View File
@@ -0,0 +1,42 @@
//! Conversions between ULID and UUID.
use crate::Ulid;
use uuid::Uuid;
impl From<Uuid> for Ulid {
fn from(uuid: Uuid) -> Self {
Ulid(uuid.as_u128())
}
}
impl From<Ulid> for Uuid {
fn from(ulid: Ulid) -> Self {
Uuid::from_u128(ulid.0)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn uuid_cycle() {
let ulid = Ulid::new();
let uuid: Uuid = ulid.into();
let ulid2: Ulid = uuid.into();
assert_eq!(ulid, ulid2);
}
#[test]
fn uuid_str_cycle() {
let uuid_txt = "771a3bce-02e9-4428-a68e-b1e7e82b7f9f";
let ulid_txt = "3Q38XWW0Q98GMAD3NHWZM2PZWZ";
let ulid: Ulid = Uuid::parse_str(uuid_txt).unwrap().into();
assert_eq!(ulid.to_string(), ulid_txt);
let uuid: Uuid = ulid.into();
assert_eq!(uuid.to_string(), uuid_txt);
}
}
+78
View File
@@ -0,0 +1,78 @@
#![cfg(all(target_arch = "wasm32", target_os = "unknown"))]
use ulid::Ulid;
use wasm_bindgen_test::*;
use web_time::web::SystemTimeExt;
use std::time::{Duration, SystemTime};
fn now() -> std::time::SystemTime {
return web_time::SystemTime::now().to_std();
}
#[wasm_bindgen_test]
fn test_dynamic() {
let ulid = Ulid::new();
let encoded = ulid.to_string();
let ulid2 = Ulid::from_string(&encoded).expect("failed to deserialize");
println!("{}", encoded);
println!("{:?}", ulid);
println!("{:?}", ulid2);
assert_eq!(ulid, ulid2);
}
#[wasm_bindgen_test]
fn test_source() {
use rand::rngs::mock::StepRng;
let mut source = StepRng::new(123, 0);
let u1 = Ulid::with_source(&mut source);
let dt = now() + Duration::from_millis(1);
let u2 = Ulid::from_datetime_with_source(dt, &mut source);
let u3 = Ulid::from_datetime_with_source(dt, &mut source);
assert!(u1 < u2);
assert_eq!(u2, u3);
}
#[wasm_bindgen_test]
fn test_order() {
let dt = now();
let ulid1 = Ulid::from_datetime(dt);
let ulid2 = Ulid::from_datetime(dt + Duration::from_millis(1));
assert!(ulid1 < ulid2);
}
#[wasm_bindgen_test]
fn test_datetime() {
let dt = now();
let ulid = Ulid::from_datetime(dt);
println!("{:?}, {:?}", dt, ulid.datetime());
assert!(ulid.datetime() <= dt);
assert!(ulid.datetime() + Duration::from_millis(1) >= dt);
}
#[wasm_bindgen_test]
fn test_timestamp() {
let dt = now();
let ulid = Ulid::from_datetime(dt);
let ts = dt
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_millis();
assert_eq!(u128::from(ulid.timestamp_ms()), ts);
}
#[wasm_bindgen_test]
fn default_is_nil() {
assert_eq!(Ulid::default(), Ulid::nil());
}
#[wasm_bindgen_test]
fn nil_is_at_unix_epoch() {
assert_eq!(Ulid::nil().datetime(), SystemTime::UNIX_EPOCH);
}