use std::fmt;
use std::hash::{Hash, Hasher};
use fnv::FnvHasher;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct NodeHash {
hash: u64,
is_wildcard: bool,
}
impl NodeHash {
const WILDCARD_HASH: u64 = 0;
#[must_use]
pub fn from_id(node_id: &str) -> Self {
let mut hasher = FnvHasher::default();
node_id.hash(&mut hasher);
let hash = hasher.finish();
let hash = if hash == Self::WILDCARD_HASH {
hash.wrapping_add(1)
} else {
hash
};
Self {
hash,
is_wildcard: false,
}
}
#[must_use]
pub fn wildcard() -> Self {
Self {
hash: Self::WILDCARD_HASH,
is_wildcard: true,
}
}
#[must_use]
pub fn is_wildcard(&self) -> bool {
self.is_wildcard
}
#[must_use]
pub fn as_u64(&self) -> u64 {
self.hash
}
}
impl fmt::Display for NodeHash {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_wildcard {
write!(f, "<wildcard>")
} else {
write!(f, "{:016x}", self.hash)
}
}
}
impl Default for NodeHash {
fn default() -> Self {
Self::wildcard()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_node_hash_creation() {
let node = NodeHash::from_id("test-node");
assert!(!node.is_wildcard());
assert_ne!(node.as_u64(), 0);
}
#[test]
fn test_node_hash_deterministic() {
let node1 = NodeHash::from_id("test-node");
let node2 = NodeHash::from_id("test-node");
assert_eq!(node1, node2);
assert_eq!(node1.as_u64(), node2.as_u64());
}
#[test]
fn test_different_nodes_different_hashes() {
let node1 = NodeHash::from_id("node-1");
let node2 = NodeHash::from_id("node-2");
assert_ne!(node1, node2);
}
#[test]
fn test_wildcard() {
let wildcard = NodeHash::wildcard();
assert!(wildcard.is_wildcard());
assert_eq!(wildcard.as_u64(), 0);
}
#[test]
fn test_display() {
let node = NodeHash::from_id("test");
let display = format!("{node}");
assert_eq!(display.len(), 16);
let wildcard = NodeHash::wildcard();
assert_eq!(format!("{wildcard}"), "<wildcard>");
}
#[test]
fn test_default_is_wildcard() {
let default = NodeHash::default();
assert!(default.is_wildcard());
}
}