use std::borrow::Cow;
use crate::error::{ID3v2Error, ID3v2ErrorKind, LoftyError, Result};
use crate::tag::item::ItemKey;
use crate::tag::TagType;
#[derive(PartialEq, Clone, Debug, Eq, Hash)]
pub enum FrameID<'a> {
Valid(Cow<'a, str>),
Outdated(Cow<'a, str>),
}
impl<'a> FrameID<'a> {
pub fn new<I>(id: I) -> Result<Self>
where
I: Into<Cow<'a, str>>,
{
Self::new_cow(id.into())
}
pub(super) fn new_cow(id: Cow<'a, str>) -> Result<Self> {
Self::verify_id(&id)?;
match id.len() {
3 => Ok(FrameID::Outdated(id)),
4 => Ok(FrameID::Valid(id)),
_ => Err(ID3v2Error::new(ID3v2ErrorKind::BadFrameID).into()),
}
}
pub fn as_str(&self) -> &str {
match self {
FrameID::Valid(v) | FrameID::Outdated(v) => v,
}
}
pub(super) fn verify_id(id_str: &str) -> Result<()> {
for c in id_str.chars() {
if !c.is_ascii_uppercase() && !c.is_ascii_digit() {
return Err(ID3v2Error::new(ID3v2ErrorKind::BadFrameID).into());
}
}
Ok(())
}
pub fn as_borrowed(&'a self) -> Self {
match self {
Self::Valid(inner) => Self::Valid(Cow::Borrowed(inner)),
Self::Outdated(inner) => Self::Outdated(Cow::Borrowed(inner)),
}
}
pub fn into_owned(self) -> FrameID<'static> {
match self {
Self::Valid(inner) => FrameID::Valid(Cow::Owned(inner.into_owned())),
Self::Outdated(inner) => FrameID::Outdated(Cow::Owned(inner.into_owned())),
}
}
}
impl<'a> TryFrom<&'a ItemKey> for FrameID<'a> {
type Error = LoftyError;
fn try_from(value: &'a ItemKey) -> std::prelude::rust_2015::Result<Self, Self::Error> {
match value {
ItemKey::Unknown(unknown) if unknown.len() == 4 => {
Self::verify_id(unknown)?;
Ok(Self::Valid(Cow::Borrowed(unknown)))
},
k => {
if let Some(mapped) = k.map_key(TagType::ID3v2, false) {
if mapped.len() == 4 {
Self::verify_id(mapped)?;
return Ok(Self::Valid(Cow::Borrowed(mapped)));
}
}
Err(ID3v2Error::new(ID3v2ErrorKind::BadFrameID).into())
},
}
}
}