use crate::io::BdatVersion;
use crate::Utf;
use std::borrow::Cow;
use std::{cmp::Ordering, fmt::Display};
#[derive(thiserror::Error, Debug)]
#[error("label is not a string")]
pub struct LabelNotStringError;
#[derive(PartialEq, Eq, Debug, Clone, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Label {
Hash(u32),
String(String),
Unhashed(String),
}
impl Label {
pub fn parse<'a, S: Into<Utf<'a>>>(text: S, force_hash: bool) -> Self {
let text = text.into();
if text.len() == 10 && text.as_bytes()[0] == b'<' {
if let Ok(n) = u32::from_str_radix(&text[1..=8], 16) {
return Label::Hash(n);
}
}
if force_hash {
Label::Hash(crate::hash::murmur3_str(&text))
} else {
Label::String(text.into_owned())
}
}
pub fn into_hash(self, version: BdatVersion) -> Self {
if !version.are_labels_hashed() {
return self;
}
match self {
l @ Self::Hash(_) => l,
Self::String(s) | Self::Unhashed(s) => Self::Hash(crate::hash::murmur3_str(&s)),
}
}
pub fn cmp_value(&self, other: &Self) -> Ordering {
match (self, other) {
(Self::Hash(slf), Self::Hash(oth)) => slf.cmp(oth),
(_, Self::Hash(_)) => Ordering::Less, (Self::Hash(_), _) => Ordering::Greater,
(a, b) => a.as_str().cmp(b.as_str()),
}
}
pub fn to_string_convert(&self) -> Utf {
match self {
Self::String(s) | Self::Unhashed(s) => Cow::Borrowed(s.as_str()),
_ => Cow::Owned(self.to_string()),
}
}
fn as_str(&self) -> &str {
self.try_into().expect("label is not a string")
}
}
impl From<String> for Label {
fn from(s: String) -> Self {
Self::String(s)
}
}
impl From<&str> for Label {
fn from(s: &str) -> Self {
s.to_string().into()
}
}
impl From<u32> for Label {
fn from(hash: u32) -> Self {
Self::Hash(hash)
}
}
impl<'s> TryFrom<&'s Label> for &'s str {
type Error = LabelNotStringError;
fn try_from(value: &'s Label) -> Result<Self, Self::Error> {
match value {
Label::String(s) | Label::Unhashed(s) => Ok(s.as_str()),
_ => Err(LabelNotStringError),
}
}
}
impl Display for Label {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Hash(hash) => {
if f.sign_plus() {
write!(f, "{:08X}", hash)
} else {
write!(f, "<{:08X}>", hash)
}
}
Self::String(s) | Self::Unhashed(s) => write!(f, "{}", s),
}
}
}