From 30aef157ad70ace8db4c79faefa7ac36661e3a60 Mon Sep 17 00:00:00 2001 From: sevki Date: Sat, 21 Sep 2024 13:02:57 +0100 Subject: [PATCH] ulid and uuid --- Cargo.lock | 7 +- Cargo.toml | 8 +- README.md | 4 +- src/blake3.rs | 28 +- src/lib.rs | 280 +++++++++++++++--- src/sha1.rs | 59 +++- src/sha2.rs | 35 ++- src/sha3.rs | 31 +- .../dgst__binary_id_tests__display.snap | 5 - ..._binary_id_tests__display_hello_world.snap | 5 - src/ulid.rs | 33 +++ src/uuid.rs | 32 ++ 12 files changed, 418 insertions(+), 109 deletions(-) delete mode 100644 src/snapshots/dgst__binary_id_tests__display.snap delete mode 100644 src/snapshots/dgst__binary_id_tests__display_hello_world.snap create mode 100644 src/ulid.rs create mode 100644 src/uuid.rs diff --git a/Cargo.lock b/Cargo.lock index 99bd36b..fa0b1ad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -225,7 +225,7 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "okid" -version = "0.1.1" +version = "0.1.3" dependencies = [ "blake3", "digest", @@ -402,6 +402,7 @@ checksum = "04f903f293d11f31c0c29e4148f6dc0d033a7f80cebc0282bea147611667d289" dependencies = [ "getrandom", "rand", + "uuid", "web-time", ] @@ -416,6 +417,10 @@ name = "uuid" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +dependencies = [ + "getrandom", + "wasm-bindgen", +] [[package]] name = "version_check" diff --git a/Cargo.toml b/Cargo.toml index 2c530b9..fb87254 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "okid" -version = "0.1.1" +version = "0.1.3" edition = "2021" publish = ["oksoftware"] @@ -14,11 +14,11 @@ serde_bytes = "0.11.15" sha1 = { version = "0.10.6", optional = true } sha2 = { version = "0.10.8", optional = true } sha3 = { version = "0.10.8", optional = true } -ulid = { version = "1.1.3", optional = true } -uuid = { version = "1.10.0", optional = true } +ulid = { version = "1.1.3", optional = true, features = ["uuid"] } +uuid = { version = "1.10.0", optional = true, features = ["js", "v4"] } [features] -default = ["sha1", "sha2", "sha3", "blake3"] +default = ["sha1", "sha2", "sha3", "blake3", "uuid", "ulid"] sha1 = ["dep:sha1"] sha2 = ["dep:sha2"] sha3 = ["dep:sha3"] diff --git a/README.md b/README.md index 9243995..ed71664 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -# okid - +# okid + IDs represented as self identifying strings. diff --git a/src/blake3.rs b/src/blake3.rs index ffdc25c..26c92d5 100644 --- a/src/blake3.rs +++ b/src/blake3.rs @@ -1,11 +1,10 @@ use std::fmt::Display; -use crate::{BinaryId, BinaryType, Digest}; +use crate::{BinaryType, Digest, OkId}; #[derive(Copy, Clone, Debug, PartialEq)] pub(super) struct Blake3([u8; 32]); - -impl From for BinaryId { +impl From for OkId { fn from(value: blake3::Hasher) -> Self { let data = value.finalize(); let data = data.as_bytes(); @@ -16,20 +15,27 @@ impl From for BinaryId { Self { hash_type: BinaryType::Blake3, - digest: Digest { - blake3: Blake3(buf), - }, + digest: Digest::Blake3(Blake3(buf)), } } } impl Display for Blake3 { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let buf = self.0; - // Write the hex digest - for byte in buf { - write!(f, "{:02x}", byte)?; - } + let data = self.0; + let buf = hex::encode(data); + f.write_str(&buf)?; Ok(()) } } + +impl std::str::FromStr for Blake3 { + type Err = crate::Error; + + fn from_str(s: &str) -> Result { + let buf = hex::decode(s)?; + let mut hash: [u8; 32] = [0; 32]; + hash.copy_from_slice(&buf); + Ok(Blake3(hash)) + } +} diff --git a/src/lib.rs b/src/lib.rs index 3c0647a..21a6909 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,17 +1,45 @@ //! okid is a library for generating cannocial self-describing binary identifiers for //! git commits, sha1, sha256, sha512 hashes, ULIDs, UUIDs, datatime, extended, //! and random identifiers, etc. +//! okid binary identifier +//! # Examples +//! ## sha1 +//! ```rust +//! use sha1::Digest as sha1digest; +//! let hasher = sha1::Sha1::new(); +//! let binary_id = okid::OkId::from(hasher); +//! insta::assert_yaml_snapshot!(binary_id.to_string(), @r###"1ː00da39a3ee5e6b4b0d3255bfef95601890afd80709"###); +//! ``` +//! ## sha256 +//! ```rust +//! use sha2::Digest; +//! let mut hasher = sha2::Sha256::new(); +//! hasher.update(b"hello world"); +//! let binary_id = okid::OkId::from(hasher); +//! insta::assert_yaml_snapshot!(binary_id.to_string(), @r###" +//! 2ː00b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9 +//! "###); +//! ``` +//! +//! The resulting strings look like this: +//! 2ː00b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9 +//! first character of the string is the type of the binary data +//! in this case 2 means sha256 +//! the rest of the string is the hexadecimal representation of the binary data +//! #![doc(html_logo_url = "https://assets.ok.software/okid.png")] #![doc(html_favicon_url = "https://assets.ok.software/okid.png")] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #![deny(missing_docs)] -use std::fmt::Display; +use std::{fmt::Display, str::FromStr}; use enumflags2::{bitflags, BitFlags}; use serde::{Deserialize, Serialize}; +const SEPARATOR: char = 'ː'; + #[cfg(feature = "blake3")] /// blake3 module pub mod blake3; @@ -24,6 +52,12 @@ pub mod sha2; #[cfg(feature = "sha3")] /// sha3 module pub mod sha3; +#[cfg(feature = "ulid")] +/// ulid module +pub mod ulid; +#[cfg(feature = "uuid")] +/// uuid module +pub mod uuid; #[repr(u8)] #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Hash)] @@ -40,6 +74,12 @@ pub(crate) enum BinaryType { #[cfg(feature = "blake3")] // Next bit means the size of the digest is of blake3 type Blake3 = 1 << 3, + #[cfg(feature = "ulid")] + // ULID + Ulid = 1 << 4, + #[cfg(feature = "uuid")] + // UUID + Uuid = 1 << 5, } impl From for BinaryType { @@ -53,6 +93,10 @@ impl From for BinaryType { '3' => Self::Sha3_512, #[cfg(feature = "blake3")] 'b' => Self::Blake3, + #[cfg(feature = "ulid")] + 'u' => Self::Ulid, + #[cfg(feature = "uuid")] + 'i' => Self::Uuid, _ => panic!("Invalid binary type"), } } @@ -69,6 +113,10 @@ impl BinaryType { Self::Sha3_512 => '3', #[cfg(feature = "blake3")] Self::Blake3 => 'b', + #[cfg(feature = "ulid")] + Self::Ulid => 'u', + #[cfg(feature = "uuid")] + Self::Uuid => 'i', } } } @@ -84,17 +132,116 @@ impl Display for BinaryType { Self::Sha3_512 => write!(f, "sha3-512"), #[cfg(feature = "blake3")] Self::Blake3 => write!(f, "blake3"), + #[cfg(feature = "ulid")] + Self::Ulid => write!(f, "ulid"), + #[cfg(feature = "uuid")] + Self::Uuid => write!(f, "uuid"), } } } -/// Bird is a binary identifier representable as data -pub struct BinaryId { +/// The digest of the binary identifier +pub struct OkId { hash_type: BinaryType, /// The digest of the binary identifier digest: Digest, } +impl Serialize for OkId { + fn serialize(&self, serializer: S) -> Result { + serializer.serialize_str(&self.to_string()) + } +} + +impl<'de> Deserialize<'de> for OkId { + fn deserialize>(deserializer: D) -> Result { + let s = String::deserialize(deserializer)?; + s.parse().map_err(serde::de::Error::custom) + } +} + +#[derive(Debug, Clone)] +/// Errors that can occur when parsing an OkId +pub enum Error { + /// The length of the OkId is invalid + InvalidLength, + /// The hash type is invalid + InvalidHashType, + /// Error parsing hex + Hex(hex::FromHexError), + /// Invalid format + InvalidFormat, +} + +impl Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Error::InvalidLength => write!(f, "Invalid length"), + Error::InvalidHashType => write!(f, "Invalid hash type"), + Error::Hex(e) => write!(f, "Hex error: {}", e), + Error::InvalidFormat => write!(f, "Invalid format"), + } + } +} + +impl From for Error { + fn from(e: hex::FromHexError) -> Self { + Error::Hex(e) + } +} + +impl FromStr for OkId { + type Err = Error; + + fn from_str(s: &str) -> Result { + parse_okid(s) + } +} + +// parse the OkId from a string +// the string should be in the format of +fn parse_okid(s: &str) -> Result { + let mut chars = s.chars(); + let hash_type: BinaryType = chars.next().unwrap().into(); + // eat the separator + if chars.next() != Some(SEPARATOR) { + return Err(Error::InvalidFormat); + } + let rest = chars.collect::(); + match hash_type { + #[cfg(feature = "sha1")] + BinaryType::Sha1 => Ok(OkId { + hash_type, + digest: Digest::Sha1(rest.parse()?), + }), + #[cfg(feature = "sha2")] + BinaryType::Sha256 => Ok(OkId { + hash_type, + digest: Digest::Sha256(rest.parse()?), + }), + #[cfg(feature = "sha3")] + BinaryType::Sha3_512 => Ok(OkId { + hash_type, + digest: Digest::Sha512(rest.parse()?), + }), + #[cfg(feature = "blake3")] + BinaryType::Blake3 => Ok(OkId { + hash_type, + digest: Digest::Blake3(rest.parse()?), + }), + #[cfg(feature = "ulid")] + BinaryType::Ulid => Ok(OkId { + hash_type, + digest: Digest::Ulid(rest.parse()?), + }), + #[cfg(feature = "uuid")] + BinaryType::Uuid => Ok(OkId { + hash_type, + digest: Digest::Uuid(rest.parse()?), + }), + } +} + #[bitflags] #[repr(u8)] #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Hash)] @@ -139,46 +286,41 @@ impl Display for CommonSettings { write!(f, "{}", buf + settings.as_str()) } } - /// Digest of the binary identifier -union Digest { - /// u64 bit fp like hashes, probably not secure - u64: u64, +#[derive(Debug, Clone)] +enum Digest { #[cfg(feature = "sha1")] - /// SHA-1 digest - sha1: crate::sha1::Sha1, + Sha1(crate::sha1::Sha1), #[cfg(feature = "sha2")] - /// SHA-256 digest - sha256: crate::sha2::Sha256, + Sha256(crate::sha2::Sha256), #[cfg(feature = "sha3")] - /// SHA-512 digest - sha512: crate::sha3::Sha512, + Sha512(crate::sha3::Sha512), #[cfg(feature = "blake3")] - blake3: crate::blake3::Blake3, + Blake3(crate::blake3::Blake3), + #[cfg(feature = "ulid")] + Ulid(crate::ulid::Ulid), + #[cfg(feature = "uuid")] + Uuid(crate::uuid::Uuid), } -impl From for Digest { - fn from(value: u64) -> Self { - Self { u64: value } - } -} - -impl Display for BinaryId { +impl Display for OkId { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { // Write the binary type code - write!(f, "{}ː", self.hash_type.char_code())?; + write!(f, "{}{}", self.hash_type.char_code(), SEPARATOR)?; - unsafe { - match self.hash_type { - #[cfg(feature = "sha1")] - BinaryType::Sha1 => self.digest.sha1.fmt(f), - #[cfg(feature = "sha2")] - BinaryType::Sha256 => self.digest.sha256.fmt(f), - #[cfg(feature = "sha3")] - BinaryType::Sha3_512 => self.digest.sha512.fmt(f), - #[cfg(feature = "blake3")] - BinaryType::Blake3 => self.digest.blake3.fmt(f), - } + match &self.digest { + #[cfg(feature = "sha1")] + Digest::Sha1(sha1) => sha1.fmt(f), + #[cfg(feature = "sha2")] + Digest::Sha256(sha256) => sha256.fmt(f), + #[cfg(feature = "sha3")] + Digest::Sha512(sha512) => sha512.fmt(f), + #[cfg(feature = "blake3")] + Digest::Blake3(blake3) => blake3.fmt(f), + #[cfg(feature = "ulid")] + Digest::Ulid(ulid) => ulid.fmt(f), + #[cfg(feature = "uuid")] + Digest::Uuid(uuid) => uuid.fmt(f), } } } @@ -189,12 +331,12 @@ mod binary_id_tests { #[cfg(feature = "sha1")] use sha1::Digest as sha1digest; - use crate::BinaryId; + use crate::OkId; #[cfg(feature = "sha1")] #[test] fn test_display() { let hasher = sha1::Sha1::new(); - let binary_id = BinaryId::from(hasher); + let binary_id = OkId::from(hasher); insta::assert_yaml_snapshot!(binary_id.to_string(), @r###" 1ː00da39a3ee5e6b4b0d3255bfef95601890afd80709 "###); @@ -204,7 +346,7 @@ mod binary_id_tests { fn test_display_hello_world() { let mut hasher = sha1::Sha1::new(); hasher.update(b"hello world"); - let binary_id = BinaryId::from(hasher); + let binary_id = OkId::from(hasher); insta::assert_yaml_snapshot!(binary_id.to_string(), @r###" 1ː002aae6c35c94fcfb415dbe95f408b9ce91ee846ed "###); @@ -214,7 +356,7 @@ mod binary_id_tests { fn test_display_hello_world_sha256() { let mut hasher = sha2::Sha256::new(); hasher.update(b"hello world"); - let binary_id = BinaryId::from(hasher); + let binary_id = OkId::from(hasher); insta::assert_yaml_snapshot!(binary_id.to_string(), @r###" 2ː00b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9 "###); @@ -225,7 +367,7 @@ mod binary_id_tests { fn test_display_hello_world_sha3() { let mut hasher = sha3::Sha3_512::new(); hasher.update(b"hello world"); - let binary_id = BinaryId::from(hasher); + let binary_id = OkId::from(hasher); insta::assert_yaml_snapshot!(binary_id.to_string(), @r###" 3ː840006653e9ac9e95117a15c915caab81662918e925de9e004f774ff82d7079a40d4d27b1b372657c61d46d470304c88c788b3a4527ad074d1dccbee5dbaa99a "###); @@ -236,7 +378,67 @@ mod binary_id_tests { fn test_display_hello_world_blake3() { let mut hasher = blake3::Hasher::new(); hasher.update(b"hello world"); - let binary_id = BinaryId::from(hasher); + let binary_id = OkId::from(hasher); + insta::assert_yaml_snapshot!(binary_id.to_string(), @r###" + bːd74981efa70a0c880b8d8c1985d075dbcbf679b99a5f9914e5aaf96b831a9e24 + "###); + } + + #[cfg(feature = "ulid")] + #[test] + fn test_display_hello_world_ulid() { + let ulid = ulid::Ulid::from_parts(0x0192146907d25d66, 0x35da136af2f988ca); + let binary_id = OkId::from(ulid); + insta::assert_yaml_snapshot!(binary_id.to_string(), @r###" + uː146907d25d66000035da136af2f988ca + "###); + } + + #[cfg(feature = "uuid")] + #[test] + fn test_display_hello_world_uuid() { + let uuid = uuid::Uuid::from_u128(0x73da51ba29654c53909fc283d33e39ba); + let binary_id = OkId::from(uuid); + insta::assert_yaml_snapshot!(binary_id.to_string(), @r###" + iː73da51ba29654c53909fc283d33e39ba + "###); + } + + #[cfg(feature = "sha1")] + #[test] + fn test_parse_hello_world_sha1() { + let hash = "1ː002aae6c35c94fcfb415dbe95f408b9ce91ee846ed"; + let binary_id = hash.parse::().unwrap(); + insta::assert_yaml_snapshot!(binary_id.to_string(), @r###" + 1ː002aae6c35c94fcfb415dbe95f408b9ce91ee846ed + "###); + } + + #[cfg(feature = "sha2")] + #[test] + fn test_parse_hello_world_sha256() { + let hash = "2ː00b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"; + let binary_id = hash.parse::().unwrap(); + insta::assert_yaml_snapshot!(binary_id.to_string(), @r###" + 2ː00b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9 + "###); + } + + #[cfg(feature = "sha3")] + #[test] + fn test_parse_hello_world_sha3() { + let hash = "3ː840006653e9ac9e95117a15c915caab81662918e925de9e004f774ff82d7079a40d4d27b1b372657c61d46d470304c88c788b3a4527ad074d1dccbee5dbaa99a"; + let binary_id = hash.parse::().unwrap(); + insta::assert_yaml_snapshot!(binary_id.to_string(), @r###" + 3ː840006653e9ac9e95117a15c915caab81662918e925de9e004f774ff82d7079a40d4d27b1b372657c61d46d470304c88c788b3a4527ad074d1dccbee5dbaa99a + "###); + } + + #[cfg(feature = "blake3")] + #[test] + fn test_parse_hello_world_blake3() { + let hash = "bːd74981efa70a0c880b8d8c1985d075dbcbf679b99a5f9914e5aaf96b831a9e24"; + let binary_id = hash.parse::().unwrap(); insta::assert_yaml_snapshot!(binary_id.to_string(), @r###" bːd74981efa70a0c880b8d8c1985d075dbcbf679b99a5f9914e5aaf96b831a9e24 "###); diff --git a/src/sha1.rs b/src/sha1.rs index 01caa7a..8d43803 100644 --- a/src/sha1.rs +++ b/src/sha1.rs @@ -1,18 +1,15 @@ -use std::fmt::Display; +use std::{fmt::Display, str::FromStr}; use digest::core_api::CoreWrapper; use enumflags2::BitFlags; -use super::{BinaryId, BinaryType, CommonSettings, Digest}; +use super::{BinaryType, CommonSettings, Digest, OkId}; #[derive(Copy, Clone, Debug, PartialEq)] pub(super) struct Sha1(BitFlags, [u8; 20]); -#[cfg(feature = "sha1")] use sha1::Digest as sha1Digest; - -#[cfg(feature = "sha1")] -impl From for BinaryId { +impl From for OkId { fn from(value: sha1::Sha1) -> Self { let data = value.finalize(); let data = data.get(0..20).unwrap(); @@ -23,13 +20,11 @@ impl From for BinaryId { let empty: BitFlags = BitFlags::empty(); Self { hash_type: BinaryType::Sha1, - digest: Digest { - sha1: Sha1(empty, buf), - }, + digest: Digest::Sha1(Sha1(empty, buf)), } } } -impl From for BinaryId { +impl From for OkId { fn from(value: sha1::Sha1Core) -> Self { CoreWrapper::from_core(value).into() } @@ -38,12 +33,44 @@ impl From for BinaryId { impl Display for Sha1 { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let settings = self.0; - let buf = self.1; - write!(f, "{:02x}", settings.bits())?; - // Write the hex digest - for byte in buf { - write!(f, "{:02x}", byte)?; - } + let data = self.1; + let buf = hex::encode([settings.bits()]) + &hex::encode(data); + f.write_str(&buf)?; Ok(()) } } + +impl FromStr for Sha1 { + type Err = super::Error; + + fn from_str(s: &str) -> Result { + let mut settings = BitFlags::empty(); + let buf = hex::decode(s)?; + let _ = buf.first().map(|&first| { + settings = BitFlags::from_bits_truncate(first); + }); + let mut hash: [u8; 20] = [0; 20]; + hash.copy_from_slice(&buf[1..]); + Ok(Sha1(settings, hash)) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn sha1_to_string() { + let hasher = sha1::Sha1::new(); + let binary_id = OkId::from(hasher); + insta::assert_yaml_snapshot!(binary_id.to_string(), @r###" + 1ː00da39a3ee5e6b4b0d3255bfef95601890afd80709 + "###); + } + + #[test] + fn sha1_from_str() { + let hash = "1ː00da39a3ee5e6b4b0d3255bfef95601890afd80709"; + let _binary_id: OkId = hash.parse().unwrap(); + } +} diff --git a/src/sha2.rs b/src/sha2.rs index e256d16..0df9d49 100644 --- a/src/sha2.rs +++ b/src/sha2.rs @@ -1,15 +1,14 @@ use enumflags2::BitFlags; use sha2::Digest; -use std::fmt::Display; +use std::{fmt::Display, str::FromStr}; -use crate::BinaryId; +use crate::OkId; use super::CommonSettings; #[derive(Copy, Clone, Debug, PartialEq)] pub(super) struct Sha256(BitFlags, [u8; 32]); - -impl From for BinaryId { +impl From for OkId { fn from(value: sha2::Sha256) -> Self { let data = value.finalize(); let data = data.get(0..32).unwrap(); @@ -20,9 +19,7 @@ impl From for BinaryId { let empty: BitFlags = BitFlags::empty(); Self { hash_type: super::BinaryType::Sha256, - digest: super::Digest { - sha256: Sha256(empty, buf), - }, + digest: super::Digest::Sha256(Sha256(empty, buf)), } } } @@ -30,12 +27,24 @@ impl From for BinaryId { impl Display for Sha256 { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let settings = self.0; - let buf = self.1; - write!(f, "{:02x}", settings.bits())?; - // Write the hex digest - for byte in buf { - write!(f, "{:02x}", byte)?; - } + let data = self.1; + let buf = hex::encode([settings.bits()]) + &hex::encode(data); + f.write_str(&buf)?; Ok(()) } } + +impl FromStr for Sha256 { + type Err = super::Error; + + fn from_str(s: &str) -> Result { + let mut settings = BitFlags::empty(); + let buf = hex::decode(s)?; + let _ = buf.first().map(|&first| { + settings = BitFlags::from_bits_truncate(first); + }); + let mut hash: [u8; 32] = [0; 32]; + hash.copy_from_slice(&buf[1..]); + Ok(Sha256(settings, hash)) + } +} diff --git a/src/sha3.rs b/src/sha3.rs index c3d00c5..443d701 100644 --- a/src/sha3.rs +++ b/src/sha3.rs @@ -1,14 +1,13 @@ -use std::fmt::Display; +use std::{fmt::Display, str::FromStr}; use digest::core_api::CoreWrapper; use sha3::Digest; -use super::BinaryId; +use super::OkId; #[derive(Copy, Clone, Debug, PartialEq)] pub(super) struct Sha512([u8; 64]); - -impl From for BinaryId { +impl From for OkId { fn from(value: sha3::Sha3_512) -> Self { let data = value.finalize(); let data = data.get(0..64).unwrap(); @@ -19,14 +18,12 @@ impl From for BinaryId { Self { hash_type: super::BinaryType::Sha3_512, - digest: super::Digest { - sha512: Sha512(buf), - }, + digest: super::Digest::Sha512(Sha512(buf)), } } } -impl From for BinaryId { +impl From for OkId { fn from(value: sha3::Sha3_512Core) -> Self { CoreWrapper::from_core(value).into() } @@ -34,11 +31,19 @@ impl From for BinaryId { impl Display for Sha512 { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let buf = self.0; - // Write the hex digest - for byte in buf { - write!(f, "{:02x}", byte)?; - } + let buf = hex::encode(self.0); + f.write_str(&buf)?; Ok(()) } } + +impl FromStr for Sha512 { + type Err = super::Error; + + fn from_str(s: &str) -> Result { + let buf = hex::decode(s)?; + let mut hash: [u8; 64] = [0; 64]; + hash.copy_from_slice(&buf); + Ok(Sha512(hash)) + } +} diff --git a/src/snapshots/dgst__binary_id_tests__display.snap b/src/snapshots/dgst__binary_id_tests__display.snap deleted file mode 100644 index 09d09ae..0000000 --- a/src/snapshots/dgst__binary_id_tests__display.snap +++ /dev/null @@ -1,5 +0,0 @@ ---- -source: src/lib.rs -expression: binary_id.to_string() ---- -1ː00da39a3ee5e6b4b0d3255bfef95601890afd80709 diff --git a/src/snapshots/dgst__binary_id_tests__display_hello_world.snap b/src/snapshots/dgst__binary_id_tests__display_hello_world.snap deleted file mode 100644 index edb44c4..0000000 --- a/src/snapshots/dgst__binary_id_tests__display_hello_world.snap +++ /dev/null @@ -1,5 +0,0 @@ ---- -source: src/lib.rs -expression: binary_id.to_string() ---- -1ː002aae6c35c94fcfb415dbe95f408b9ce91ee846ed diff --git a/src/ulid.rs b/src/ulid.rs new file mode 100644 index 0000000..df7e7e5 --- /dev/null +++ b/src/ulid.rs @@ -0,0 +1,33 @@ +use std::fmt::Display; + +use crate::OkId; + +#[derive(Copy, Clone, Debug, PartialEq)] +pub(super) struct Ulid(u128); + +impl From for OkId { + fn from(value: ulid::Ulid) -> Self { + Self { + hash_type: super::BinaryType::Ulid, + digest: super::Digest::Ulid(Ulid(value.into())), + } + } +} + +impl Display for Ulid { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let buf = hex::encode(self.0.to_be_bytes()); + write!(f, "{}", buf) + } +} + +impl std::str::FromStr for Ulid { + type Err = crate::Error; + + fn from_str(s: &str) -> Result { + let buf = hex::decode(s)?; + let mut hash: [u8; 16] = [0; 16]; + hash.copy_from_slice(&buf); + Ok(Ulid(u128::from_be_bytes(hash))) + } +} diff --git a/src/uuid.rs b/src/uuid.rs new file mode 100644 index 0000000..8296e7b --- /dev/null +++ b/src/uuid.rs @@ -0,0 +1,32 @@ +use std::fmt::Display; + +use crate::OkId; + +#[derive(Copy, Clone, Debug, PartialEq)] +pub(super) struct Uuid(u128); +impl From for OkId { + fn from(value: uuid::Uuid) -> Self { + Self { + hash_type: super::BinaryType::Uuid, + digest: super::Digest::Uuid(Uuid(value.as_u128())), + } + } +} + +impl Display for Uuid { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let buf = hex::encode(self.0.to_be_bytes()); + write!(f, "{}", buf) + } +} + +impl std::str::FromStr for Uuid { + type Err = crate::Error; + + fn from_str(s: &str) -> Result { + let buf = hex::decode(s)?; + let mut hash: [u8; 16] = [0; 16]; + hash.copy_from_slice(&buf); + Ok(Uuid(u128::from_be_bytes(hash))) + } +}