use std::fmt::{Display, Formatter};
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct AccountId(u64);
impl AccountId {
pub fn from_u64(value: u64) -> Self {
Self(value)
}
#[allow(clippy::should_implement_trait)]
pub fn from_str(value: impl AsRef<str>) -> Self {
Self(fnv1a_64(value.as_ref()))
}
pub fn as_u64(self) -> u64 {
self.0
}
}
impl From<u64> for AccountId {
fn from(value: u64) -> Self {
Self::from_u64(value)
}
}
impl Display for AccountId {
fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
Display::fmt(&self.0, formatter)
}
}
fn fnv1a_64(s: &str) -> u64 {
const OFFSET_BASIS: u64 = 14_695_981_039_346_656_037;
const PRIME: u64 = 1_099_511_628_211;
let mut hash = OFFSET_BASIS;
for byte in s.bytes() {
hash ^= u64::from(byte);
hash = hash.wrapping_mul(PRIME);
}
hash
}
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use super::AccountId;
#[test]
fn from_u64_display_shows_integer() {
assert_eq!(AccountId::from_u64(99224416).to_string(), "99224416");
assert_eq!(AccountId::from_u64(42).to_string(), "42");
assert_eq!(
AccountId::from_u64(u64::MAX).to_string(),
u64::MAX.to_string()
);
}
#[test]
fn from_u64_equality() {
assert_eq!(AccountId::from_u64(7), AccountId::from_u64(7));
assert_ne!(AccountId::from_u64(7), AccountId::from_u64(8));
}
#[test]
fn from_str_same_string_equal() {
assert_eq!(
AccountId::from_str("account-a"),
AccountId::from_str("account-a")
);
}
#[test]
fn from_str_different_strings_not_equal() {
assert_ne!(
AccountId::from_str("account-a"),
AccountId::from_str("account-b")
);
}
#[test]
fn from_u64_and_from_str_of_same_numeric_string_differ() {
assert_ne!(AccountId::from_u64(42), AccountId::from_str("42"));
}
#[test]
fn hashmap_lookup_with_from_u64() {
let mut map: HashMap<AccountId, &str> = HashMap::new();
map.insert(AccountId::from_u64(100), "alpha");
assert_eq!(map[&AccountId::from_u64(100)], "alpha");
}
#[test]
fn hashmap_lookup_with_from_str() {
let mut map: HashMap<AccountId, &str> = HashMap::new();
map.insert(AccountId::from_str("beta"), "beta-value");
assert_eq!(map[&AccountId::from_str("beta")], "beta-value");
}
#[test]
fn fnv1a_empty_string_equals_offset_basis() {
assert_eq!(
AccountId::from_str(""),
AccountId::from_u64(14_695_981_039_346_656_037)
);
}
#[test]
fn as_u64_returns_inner_value() {
assert_eq!(AccountId::from_u64(99).as_u64(), 99);
assert_eq!(AccountId::from_u64(99224416).as_u64(), 99224416);
assert_eq!(AccountId::from_u64(u64::MAX).as_u64(), u64::MAX);
}
#[test]
fn from_u64_trait_delegates_to_constructor() {
let via_trait: AccountId = AccountId::from(42u64);
let via_constructor = AccountId::from_u64(42);
assert_eq!(via_trait, via_constructor);
}
}