use std::{
fmt,
hash::{Hash, Hasher},
str::FromStr,
};
use crate::{node_id::id_ref::IdentifierRef, ByteString, Guid, UAString};
#[derive(Eq, PartialEq, Clone, Debug)]
pub enum Identifier {
Numeric(u32),
String(UAString),
Guid(Guid),
ByteString(ByteString),
}
pub const IDENTIFIER_HASH_NUMERIC: u8 = 0;
pub const IDENTIFIER_HASH_STRING: u8 = 1;
pub const IDENTIFIER_HASH_GUID: u8 = 2;
pub const IDENTIFIER_HASH_BYTE_STRING: u8 = 3;
impl std::hash::Hash for Identifier {
fn hash<H: Hasher>(&self, state: &mut H) {
match self {
Identifier::Numeric(v) => {
IDENTIFIER_HASH_NUMERIC.hash(state);
v.hash(state)
}
Identifier::String(v) => {
IDENTIFIER_HASH_STRING.hash(state);
v.as_ref().hash(state)
}
Identifier::Guid(v) => {
IDENTIFIER_HASH_GUID.hash(state);
v.to_bytes().hash(state)
}
Identifier::ByteString(v) => {
IDENTIFIER_HASH_BYTE_STRING.hash(state);
v.as_ref().hash(state)
}
}
}
}
impl fmt::Display for Identifier {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Identifier::Numeric(v) => write!(f, "i={}", *v),
Identifier::String(v) => write!(f, "s={v}"),
Identifier::Guid(v) => write!(f, "g={v:?}"),
Identifier::ByteString(v) => write!(f, "b={}", v.as_base64()),
}
}
}
impl FromStr for Identifier {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.len() < 2 {
Err(())
} else {
let k = &s[..2];
let v = &s[2..];
match k {
"i=" => v.parse::<u32>().map(|v| v.into()).map_err(|_| ()),
"s=" => Ok(UAString::from(v).into()),
"g=" => Guid::from_str(v).map(|v| v.into()).map_err(|_| ()),
"b=" => ByteString::from_base64(v).map(|v| v.into()).ok_or(()),
_ => Err(()),
}
}
}
}
impl From<u32> for Identifier {
fn from(v: u32) -> Self {
Identifier::Numeric(v)
}
}
impl<'a> From<&'a str> for Identifier {
fn from(v: &'a str) -> Self {
Identifier::from(UAString::from(v))
}
}
impl From<&String> for Identifier {
fn from(v: &String) -> Self {
Identifier::from(UAString::from(v))
}
}
impl From<String> for Identifier {
fn from(v: String) -> Self {
Identifier::from(UAString::from(v))
}
}
impl From<UAString> for Identifier {
fn from(v: UAString) -> Self {
Identifier::String(v)
}
}
impl From<Guid> for Identifier {
fn from(v: Guid) -> Self {
Identifier::Guid(v)
}
}
impl From<ByteString> for Identifier {
fn from(v: ByteString) -> Self {
Identifier::ByteString(v)
}
}
macro_rules! impl_identifier_ref {
($x:ident, $t:ty, $h:ident, $eq_p:pat, $h_expr:expr) => {
impl_identifier_ref!($x, $t, $h, $eq_p, $h_expr, $x);
};
($x:ident, $t:ty, $h:ident, $eq_p:pat, $h_expr:expr, $eq_expr:expr) => {
impl PartialEq<Identifier> for $t {
fn eq(&self, other: &Identifier) -> bool {
match other {
$eq_p => $eq_expr == self,
_ => false,
}
}
}
impl IdentifierRef for $t {
fn hash_as_identifier<H: Hasher>(&self, state: &mut H) {
$h.hash(state);
let $x = self;
$h_expr.hash(state);
}
}
};
}
impl_identifier_ref!(x, u32, IDENTIFIER_HASH_NUMERIC, Identifier::Numeric(x), x);
impl_identifier_ref!(
x,
&u32,
IDENTIFIER_HASH_NUMERIC,
Identifier::Numeric(x),
x,
&x
);
impl_identifier_ref!(
x,
UAString,
IDENTIFIER_HASH_STRING,
Identifier::String(x),
x.as_ref()
);
impl_identifier_ref!(
x,
&UAString,
IDENTIFIER_HASH_STRING,
Identifier::String(x),
x.as_ref(),
&x
);
impl_identifier_ref!(
x,
String,
IDENTIFIER_HASH_STRING,
Identifier::String(x),
x.as_str(),
x.as_ref()
);
impl_identifier_ref!(x, &str, IDENTIFIER_HASH_STRING, Identifier::String(x), x);
impl_identifier_ref!(x, &String, IDENTIFIER_HASH_STRING, Identifier::String(x), x);
impl_identifier_ref!(x, Guid, IDENTIFIER_HASH_GUID, Identifier::Guid(x), x);
impl_identifier_ref!(
x,
ByteString,
IDENTIFIER_HASH_BYTE_STRING,
Identifier::ByteString(x),
x
);
impl_identifier_ref!(x, &Guid, IDENTIFIER_HASH_GUID, Identifier::Guid(x), x, &x);
impl_identifier_ref!(
x,
&ByteString,
IDENTIFIER_HASH_BYTE_STRING,
Identifier::ByteString(x),
x,
&x
);
impl_identifier_ref!(
x,
&[u8],
IDENTIFIER_HASH_BYTE_STRING,
Identifier::ByteString(x),
x
);
impl IdentifierRef for Identifier {
fn hash_as_identifier<H: Hasher>(&self, state: &mut H) {
self.hash(state);
}
}
impl PartialEq<Identifier> for &Identifier {
fn eq(&self, other: &Identifier) -> bool {
(*self).eq(other)
}
}
impl IdentifierRef for &Identifier {
fn hash_as_identifier<H: Hasher>(&self, state: &mut H) {
self.hash(state);
}
}