diff --git a/Cargo.lock b/Cargo.lock index c3cca2d..03f1ccf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -261,6 +261,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + [[package]] name = "colored" version = "1.9.4" @@ -1068,12 +1074,31 @@ version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +[[package]] +name = "mac_address" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8836fae9d0d4be2c8b4efcdd79e828a2faa058a90d005abf42f91cac5493a08e" +dependencies = [ + "nix", + "winapi", +] + [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + [[package]] name = "mime" version = "0.3.17" @@ -1118,6 +1143,19 @@ dependencies = [ "version_check", ] +[[package]] +name = "nix" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", + "memoffset", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -1165,6 +1203,7 @@ dependencies = [ "hex", "insta", "jetstream_wireformat", + "mac_address", "serde", "serde_json", "sha1", diff --git a/Cargo.toml b/Cargo.toml index 279dda2..ee466a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ enumflags2 = "0.7.10" git2 = { version = "0.18.3", optional = true, default-features = false } hex = { version = "0.4.3", features = ["serde"] } jetstream_wireformat = "6.0.0" +mac_address = { version = "1.1.7", optional = true } serde = { version = "1.0.210", features = ["derive"] } serde_json = "1.0.128" sha1 = { version = "0.10.6", optional = true } @@ -35,6 +36,7 @@ ulid = ["dep:ulid"] openapi = ["dep:utoipa"] git = ["dep:git2"] graphql = ["dep:async-graphql"] +node = ["dep:mac_address"] [dev-dependencies] insta = { version = "1.40.0", features = ["yaml"] } diff --git a/src/lib.rs b/src/lib.rs index b42b310..710a00d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,7 +4,7 @@ #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #![deny(missing_docs)] -use std::{fmt::Display, hash::Hash, path::PathBuf, str::FromStr}; +use std::{fmt::Display, hash::Hash, str::FromStr}; use digest::OutputSizeUser; @@ -28,6 +28,9 @@ pub const SEPARATOR: char = 'ː'; pub mod blake3; /// fingerprint module pub mod fingerprint; +#[cfg(feature = "node")] +/// node module +pub mod node; #[cfg(feature = "git")] /// git module pub mod oid; @@ -72,6 +75,8 @@ pub(crate) enum BinaryType { Uuid = 1 << 5, // Fingerprint Fingerprint = 1 << 6, + #[cfg(feature = "node")] + Node = 1 << 7, } impl From for BinaryType { @@ -90,6 +95,7 @@ impl From for BinaryType { #[cfg(feature = "uuid")] 'i' => Self::Uuid, 'f' => Self::Fingerprint, + 'n' => Self::Node, _ => Self::Unknown, } } @@ -99,19 +105,21 @@ impl BinaryType { fn char_code(&self) -> char { match self { #[cfg(feature = "sha1")] - Self::Sha1 => '1', + BinaryType::Sha1 => '1', #[cfg(feature = "sha2")] - Self::Sha256 => '2', + BinaryType::Sha256 => '2', #[cfg(feature = "sha3")] - Self::Sha3_512 => '3', + BinaryType::Sha3_512 => '3', #[cfg(feature = "blake3")] - Self::Blake3 => 'b', + BinaryType::Blake3 => 'b', #[cfg(feature = "ulid")] - Self::Ulid => 'u', + BinaryType::Ulid => 'u', #[cfg(feature = "uuid")] - Self::Uuid => 'i', - Self::Unknown => '0', - Self::Fingerprint => 'f', + BinaryType::Uuid => 'i', + BinaryType::Unknown => '0', + BinaryType::Fingerprint => 'f', + #[cfg(feature = "node")] + BinaryType::Node => 'n', } } } @@ -120,19 +128,21 @@ impl Display for BinaryType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { #[cfg(feature = "sha1")] - Self::Sha1 => write!(f, "sha1"), + BinaryType::Sha1 => write!(f, "sha1"), #[cfg(feature = "sha2")] - Self::Sha256 => write!(f, "sha256"), + BinaryType::Sha256 => write!(f, "sha256"), #[cfg(feature = "sha3")] - Self::Sha3_512 => write!(f, "sha3-512"), + BinaryType::Sha3_512 => write!(f, "sha3-512"), #[cfg(feature = "blake3")] - Self::Blake3 => write!(f, "blake3"), + BinaryType::Blake3 => write!(f, "blake3"), #[cfg(feature = "ulid")] - Self::Ulid => write!(f, "ulid"), + BinaryType::Ulid => write!(f, "ulid"), #[cfg(feature = "uuid")] - Self::Uuid => write!(f, "uuid"), - Self::Unknown => write!(f, "unknown"), - Self::Fingerprint => write!(f, "fingerprint"), + BinaryType::Uuid => write!(f, "uuid"), + BinaryType::Unknown => write!(f, "unknown"), + BinaryType::Fingerprint => write!(f, "fingerprint"), + #[cfg(feature = "node")] + BinaryType::Node => write!(f, "node"), } } } @@ -193,6 +203,8 @@ impl PartialEq for OkId { (Digest::Uuid(_), _) => false, (Digest::Fingerprint(a), Digest::Fingerprint(b)) => a == b, (Digest::Fingerprint(_), _) => false, + (Digest::Node(a), Digest::Node(b)) => a == b, + (Digest::Node(_), _) => false, } } } @@ -219,6 +231,7 @@ impl Hash for OkId { Digest::Ulid(d) => d.hash(state), Digest::Uuid(d) => d.hash(state), Digest::Fingerprint(d) => d.hash(state), + Digest::Node(d) => d.hash(state), } } } @@ -322,6 +335,10 @@ fn parse_okid(s: &str) -> Result { hash_type, digest: Digest::Fingerprint(rest.parse()?), }), + BinaryType::Node => Ok(OkId { + hash_type, + digest: Digest::Node(rest.parse()?), + }), } } @@ -341,6 +358,8 @@ enum Digest { #[cfg(feature = "uuid")] Uuid(crate::uuid::Uuid), Fingerprint(crate::fingerprint::Fingerprint), + #[cfg(feature = "node")] + Node(crate::node::Node), } impl Display for OkId { @@ -361,6 +380,8 @@ impl Display for OkId { #[cfg(feature = "uuid")] Digest::Uuid(uuid) => uuid.fmt(f), Digest::Fingerprint(fingerprint) => fingerprint.fmt(f), + #[cfg(feature = "node")] + Digest::Node(node) => node.fmt(f), } } } @@ -384,6 +405,8 @@ impl std::fmt::Debug for OkId { Digest::Uuid(uuid) => std::fmt::Display::fmt(uuid, f), Digest::Fingerprint(fingerprint) => std::fmt::Display::fmt(fingerprint, f), + #[cfg(feature = "node")] + Digest::Node(node) => std::fmt::Display::fmt(node, f), } } } @@ -428,6 +451,7 @@ impl jetstream_wireformat::WireFormat for OkId { Digest::Ulid(_ulid) => 128 / 8, Digest::Uuid(_uuid) => 128 / 8, Digest::Fingerprint(_fingerprint) => 64 / 8, + Digest::Node(_node) => 6 , } } @@ -453,6 +477,9 @@ impl jetstream_wireformat::WireFormat for OkId { Digest::Fingerprint(fingerprint) => { u64::encode(&fingerprint.0, writer)?; } + Digest::Node(node) => { + Data::encode(&Data(node.0.into()), writer)?; + } } Ok(()) @@ -536,6 +563,18 @@ impl jetstream_wireformat::WireFormat for OkId { digest: Digest::Fingerprint(crate::fingerprint::Fingerprint(data)), }) } + BinaryType::Node => { + let data = Data::decode(reader)?; + let data = data.get(0..6).unwrap(); + let mut buf = [0; 6]; + if data.len() == 6 { + buf.copy_from_slice(data); + } + Ok(OkId { + hash_type: BinaryType::Node, + digest: Digest::Node(crate::node::Node(buf)), + }) + } } } } @@ -806,4 +845,14 @@ mod okid_tests { assert_eq!(file, new_file); } + + #[cfg(feature = "node")] + #[test] + fn test_node_display() { + use mac_address::MacAddressIterator; + let binary_id = OkId::from( + MacAddressIterator::new().unwrap_or_else(|_| panic!("No mac address found")), + ); + assert_eq!(binary_id.to_string().len(), 15); + } } diff --git a/src/node.rs b/src/node.rs new file mode 100644 index 0000000..bb8691d --- /dev/null +++ b/src/node.rs @@ -0,0 +1,78 @@ +use std::{fmt::Display, str::FromStr}; + +use mac_address::{MacAddress, MacAddressIterator}; + +use crate::OkId; + +/// Node ID type. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub(super) struct Node(pub(crate) [u8; 6]); + +impl From for OkId { + fn from(mac: MacAddress) -> Self { + OkId { + hash_type: crate::BinaryType::Node, + digest: crate::Digest::Node(Node(mac.bytes())), + } + } +} + +impl From for OkId { + fn from(iter: MacAddressIterator) -> Self { + let mut iter = iter.into_iter(); + let mut bytes_now = iter.next().unwrap().bytes(); + for bytes in iter { + bytes_now + .iter_mut() + .zip(bytes.bytes().iter()) + .for_each(|(a, b)| *a ^= b); + } + OkId { + hash_type: crate::BinaryType::Node, + digest: crate::Digest::Node(Node(bytes_now)), + } + } +} + +impl Display for Node { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + hex::encode(self.0).fmt(f) + } +} + +impl FromStr for Node { + type Err = super::Error; + + fn from_str(s: &str) -> Result { + Ok(Node( + MacAddress::from_str(s) + .map_err(|_| super::Error::InvalidFormat)? + .bytes(), + )) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_node_from_mac_address() { + let mac = MacAddress::new([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]); + let node = OkId::from(mac); + assert_eq!( + node, + OkId { + hash_type: crate::BinaryType::Node, + digest: crate::Digest::Node(Node([0x00, 0x11, 0x22, 0x33, 0x44, 0x55])), + } + ); + } + + #[test] + fn test_node_from_mac_address_iterator() { + let iter = MacAddressIterator::new().unwrap(); + let id = OkId::from(iter); + assert_eq!(id.hash_type, crate::BinaryType::Node); + } +}