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)))
+ }
+}