pax_global_header00006660000000000000000000000064147376615510014531gustar00rootroot0000000000000052 comment=8fc11a91dbd48929221c4b8df4b284624d351ca3 ulid-rs-1.1.4/000077500000000000000000000000001473766155100131135ustar00rootroot00000000000000ulid-rs-1.1.4/.github/000077500000000000000000000000001473766155100144535ustar00rootroot00000000000000ulid-rs-1.1.4/.github/curl_options000066400000000000000000000001061473766155100171130ustar00rootroot00000000000000--fail --location --retry 3 --retry-connrefused --show-error --silent ulid-rs-1.1.4/.github/workflows/000077500000000000000000000000001473766155100165105ustar00rootroot00000000000000ulid-rs-1.1.4/.github/workflows/ci-build.yml000066400000000000000000000011551473766155100207250ustar00rootroot00000000000000name: 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 ulid-rs-1.1.4/.gitignore000066400000000000000000000000521473766155100151000ustar00rootroot00000000000000target/ **/*.rs.bk Cargo.lock .idea *.iml ulid-rs-1.1.4/Cargo.toml000066400000000000000000000020671473766155100150500ustar00rootroot00000000000000[package] name = "ulid" version = "1.1.4" authors = ["dylanhart "] 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"] [dependencies] serde = { version = "1.0", optional = true } rand = { version = "0.8", optional = true } uuid = { version = "1.1", optional = true } postgres-types = { version = "0.2.6", optional = true } bytes = { version = "1.4.0", 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.2", features = ["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 ulid-rs-1.1.4/LICENSE000066400000000000000000000020531473766155100141200ustar00rootroot00000000000000MIT 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. ulid-rs-1.1.4/README.md000066400000000000000000000041141473766155100143720ustar00rootroot00000000000000# 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) ``` ulid-rs-1.1.4/benches/000077500000000000000000000000001473766155100145225ustar00rootroot00000000000000ulid-rs-1.1.4/benches/bench.rs000066400000000000000000000017701473766155100161540ustar00rootroot00000000000000use 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); ulid-rs-1.1.4/cli/000077500000000000000000000000001473766155100136625ustar00rootroot00000000000000ulid-rs-1.1.4/cli/Cargo.toml000066400000000000000000000007141473766155100156140ustar00rootroot00000000000000[package] name = "ulid-cli" version = "0.3.1" authors = ["dylanhart ", "Kan-Ru Chen "] license = "MIT" readme = "../README.md" description = "A command line interface for generating and inspecting ULIDs" keywords = ["ulid", "uuid", "sortable", "identifier"] repository = "https://github.com/dylanhart/ulid-rs" edition = "2018" [dependencies] structopt = "0.2" ulid = { version = "*", path = ".." } time = "0.3.11" ulid-rs-1.1.4/cli/src/000077500000000000000000000000001473766155100144515ustar00rootroot00000000000000ulid-rs-1.1.4/cli/src/bin/000077500000000000000000000000001473766155100152215ustar00rootroot00000000000000ulid-rs-1.1.4/cli/src/bin/ulid.rs000066400000000000000000000044761473766155100165370ustar00rootroot00000000000000extern crate structopt; use std::io::{self, Write}; use ulid::{Generator, Ulid}; use std::{thread, time::Duration}; use structopt::StructOpt; #[derive(Debug, StructOpt)] struct Opt { /// Number of ULIDs to generate #[structopt(short = "n", long = "count", default_value = "1")] count: u32, #[structopt(short = "m", long = "monotonic")] monotonic: bool, /// ULIDs for inspection #[structopt(conflicts_with = "count")] ulids: Vec, } fn main() { let opt = Opt::from_args(); if !opt.ulids.is_empty() { inspect(&opt.ulids); } else { generate(opt.count, opt.monotonic); } } fn generate(count: u32, monotonic: bool) { let stdout = io::stdout(); let stderr = io::stderr(); let mut locked = stdout.lock(); let mut err_locked = stderr.lock(); if monotonic { let mut gen = Generator::new(); let mut i = 0; while i < count { match gen.generate() { Ok(ulid) => { writeln!(&mut locked, "{}", ulid).unwrap(); i += 1; } Err(_) => { writeln!( &mut err_locked, "Failed to create new ulid due to overflow, sleeping 1 ms" ) .unwrap(); thread::sleep(Duration::from_millis(1)); // do not increment i } } } } else { for _ in 0..count { writeln!(&mut locked, "{}", Ulid::new()).unwrap(); } } } fn inspect(values: &[String]) { for val in values { let ulid = Ulid::from_string(val); match ulid { Ok(ulid) => { let upper_hex = format!("{:X}", ulid.0); println!( " REPRESENTATION: String: {} Raw: {} COMPONENTS: Time: {} Timestamp: {} Payload: {} ", ulid.to_string(), upper_hex, time::OffsetDateTime::from(ulid.datetime()), ulid.timestamp_ms(), upper_hex.chars().skip(6).collect::() ); } Err(e) => { println!("{} is not a valid ULID: {}", val, e); } } } } ulid-rs-1.1.4/src/000077500000000000000000000000001473766155100137025ustar00rootroot00000000000000ulid-rs-1.1.4/src/base32.rs000066400000000000000000000151671473766155100153410ustar00rootroot00000000000000use 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 { // 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 { 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) ); } } ulid-rs-1.1.4/src/generator.rs000066400000000000000000000143501473766155100162410ustar00rootroot00000000000000use 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 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 { 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 { self.generate_from_datetime_with_source(datetime, &mut rand::thread_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_entropy(); /// 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(&mut self, source: &mut R) -> Result 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_entropy(); /// 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( &mut self, datetime: SystemTime, source: &mut R, ) -> Result 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); } } ulid-rs-1.1.4/src/lib.rs000066400000000000000000000266031473766155100150250ustar00rootroot00000000000000#![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 = "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)] 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 { 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 { 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 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 for (u64, u64) { fn from(ulid: Ulid) -> (u64, u64) { ((ulid.0 >> 64) as u64, (ulid.0 & bitmask!(64)) as u64) } } impl From for Ulid { fn from(value: u128) -> Ulid { Ulid(value) } } impl From 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 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 { Ulid::from_string(s) } } impl TryFrom<&'_ str> for Ulid { type Error = DecodeError; fn try_from(value: &'_ str) -> Result { 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); } } ulid-rs-1.1.4/src/postgres.rs000066400000000000000000000025671473766155100161300ustar00rootroot00000000000000use 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> { 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> { 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::>(); assert_eq!(ulid, Ulid::from_sql(t, &bs).unwrap()); } } ulid-rs-1.1.4/src/serde.rs000066400000000000000000000066551473766155100153660ustar00rootroot00000000000000//! 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(&self, serializer: S) -> Result 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(deserializer: D) -> Result 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(value: &Ulid, serializer: S) -> Result where S: Serializer, { value.0.serialize(serializer) } /// Deserializes a ULID from a u128 type. pub fn deserialize<'de, D>(deserializer: D) -> Result 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(value: &Ulid, serializer: S) -> Result 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 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)) } } ulid-rs-1.1.4/src/time.rs000066400000000000000000000125241473766155100152120ustar00rootroot00000000000000use 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_entropy(); /// let ulid = Ulid::with_source(&mut rng); /// ``` pub fn with_source(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::thread_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_entropy(); /// let ulid = Ulid::from_datetime_with_source(SystemTime::now(), &mut rng); /// ``` pub fn from_datetime_with_source(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.gen::()); let lsb = source.gen::(); 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) } } } ulid-rs-1.1.4/src/time_utils.rs000066400000000000000000000005101473766155100164220ustar00rootroot00000000000000pub(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(); } ulid-rs-1.1.4/src/uuid.rs000066400000000000000000000015441473766155100152220ustar00rootroot00000000000000//! Conversions between ULID and UUID. use crate::Ulid; use uuid::Uuid; impl From for Ulid { fn from(uuid: Uuid) -> Self { Ulid(uuid.as_u128()) } } impl From 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); } } ulid-rs-1.1.4/tests/000077500000000000000000000000001473766155100142555ustar00rootroot00000000000000ulid-rs-1.1.4/tests/wasm32-datetime.rs000066400000000000000000000035271473766155100175400ustar00rootroot00000000000000#![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); }