use crate::io::BdatVersion;
use crate::Utf;
use std::borrow::Cow;
use std::fmt::Display;
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Label<'buf> {
Hash(u32),
String(Utf<'buf>),
}
#[derive(thiserror::Error, Debug)]
#[error("label is not a string")]
pub struct LabelNotStringError;
impl<'buf> Label<'buf> {
pub fn parse<S: Into<Utf<'buf>>>(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)
}
}
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::Hash(crate::hash::murmur3_str(&s)),
}
}
pub fn to_string_convert(&self) -> Utf {
match self {
Self::String(s) => Cow::Borrowed(s.as_ref()),
_ => Cow::Owned(self.to_string()),
}
}
pub fn into_owned(self) -> Label<'static> {
match self {
Label::Hash(h) => Label::Hash(h),
Label::String(s) => Label::String(s.into_owned().into()),
}
}
pub fn as_ref(&self) -> Label {
match self {
Self::Hash(h) => Label::Hash(*h),
Self::String(s) => Label::String(s.as_ref().into()),
}
}
}
impl<'a> From<&'a Label<'_>> for Label<'a> {
fn from(value: &'a Label) -> Self {
value.as_ref()
}
}
impl<'buf> From<&'buf str> for Label<'buf> {
fn from(s: &'buf str) -> Self {
Self::String(s.into())
}
}
impl<'buf> From<String> for Label<'buf> {
fn from(s: String) -> Self {
Self::String(s.into())
}
}
impl<'buf> From<Utf<'buf>> for Label<'buf> {
fn from(value: Utf<'buf>) -> Self {
Self::String(value)
}
}
impl<'buf> From<u32> for Label<'buf> {
fn from(hash: u32) -> Self {
Self::Hash(hash)
}
}
impl<'s> TryFrom<Label<'s>> for Utf<'s> {
type Error = LabelNotStringError;
fn try_from(value: Label<'s>) -> Result<Self, Self::Error> {
match value {
Label::String(s) => Ok(s),
_ => Err(LabelNotStringError),
}
}
}
impl<'s> TryFrom<&'s Label<'s>> for &'s str {
type Error = LabelNotStringError;
fn try_from(value: &'s Label<'s>) -> Result<Self, Self::Error> {
match value {
Label::String(s) => Ok(s.as_ref()),
_ => Err(LabelNotStringError),
}
}
}
impl<'buf> Display for Label<'buf> {
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) => write!(f, "{}", s),
}
}
}