use std::array::TryFromSliceError;
use std::borrow::Cow;
use std::convert::TryInto;
use std::fmt::{self, Write};
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use std::str::FromStr;
pub(crate) const FILETYPE: Fourcc = Fourcc(*b"ftyp");
pub(crate) const MEDIA_DATA: Fourcc = Fourcc(*b"mdat");
pub(crate) const MOVIE: Fourcc = Fourcc(*b"moov");
pub(crate) const MOVIE_HEADER: Fourcc = Fourcc(*b"mvhd");
pub(crate) const TRACK: Fourcc = Fourcc(*b"trak");
pub(crate) const TRACK_HEADER: Fourcc = Fourcc(*b"tkhd");
pub(crate) const TRACK_REFERENCE: Fourcc = Fourcc(*b"tref");
pub(crate) const CHAPTER_REFERENCE: Fourcc = Fourcc(*b"chap");
pub(crate) const MEDIA: Fourcc = Fourcc(*b"mdia");
pub(crate) const MEDIA_HEADER: Fourcc = Fourcc(*b"mdhd");
pub(crate) const MEDIA_INFORMATION: Fourcc = Fourcc(*b"minf");
pub(crate) const BASE_MEDIA_INFORMATION_HEADER: Fourcc = Fourcc(*b"gmhd");
pub(crate) const BASE_MEDIA_INFORMATION: Fourcc = Fourcc(*b"gmin");
pub(crate) const DATA_INFORMATION: Fourcc = Fourcc(*b"dinf");
pub(crate) const DATA_REFERENCE: Fourcc = Fourcc(*b"dref");
pub(crate) const URL_MEDIA: Fourcc = Fourcc(*b"url ");
pub(crate) const SAMPLE_TABLE: Fourcc = Fourcc(*b"stbl");
pub(crate) const SAMPLE_TABLE_SAMPLE_SIZE: Fourcc = Fourcc(*b"stsz");
pub(crate) const SAMPLE_TABLE_SAMPLE_TO_CHUNK: Fourcc = Fourcc(*b"stsc");
pub(crate) const SAMPLE_TABLE_CHUNK_OFFSET: Fourcc = Fourcc(*b"stco");
pub(crate) const SAMPLE_TABLE_CHUNK_OFFSET_64: Fourcc = Fourcc(*b"co64");
pub(crate) const SAMPLE_TABLE_TIME_TO_SAMPLE: Fourcc = Fourcc(*b"stts");
pub(crate) const SAMPLE_TABLE_SAMPLE_DESCRIPTION: Fourcc = Fourcc(*b"stsd");
pub(crate) const MP4_AUDIO: Fourcc = Fourcc(*b"mp4a");
pub(crate) const TEXT_MEDIA: Fourcc = Fourcc(*b"text");
pub(crate) const ELEMENTARY_STREAM_DESCRIPTION: Fourcc = Fourcc(*b"esds");
pub(crate) const USER_DATA: Fourcc = Fourcc(*b"udta");
pub(crate) const CHAPTER_LIST: Fourcc = Fourcc(*b"chpl");
pub(crate) const METADATA: Fourcc = Fourcc(*b"meta");
pub(crate) const HANDLER_REFERENCE: Fourcc = Fourcc(*b"hdlr");
pub(crate) const ITEM_LIST: Fourcc = Fourcc(*b"ilst");
pub(crate) const DATA: Fourcc = Fourcc(*b"data");
pub(crate) const MEAN: Fourcc = Fourcc(*b"mean");
pub(crate) const NAME: Fourcc = Fourcc(*b"name");
pub(crate) const FREE: Fourcc = Fourcc(*b"free");
pub const FREEFORM: Fourcc = Fourcc(*b"----");
pub const ADVISORY_RATING: Fourcc = Fourcc(*b"rtng");
pub const ALBUM: Fourcc = Fourcc(*b"\xa9alb");
pub const ALBUM_ARTIST: Fourcc = Fourcc(*b"aART");
pub const ARTIST: Fourcc = Fourcc(*b"\xa9ART");
pub const ARTWORK: Fourcc = Fourcc(*b"covr");
pub const BPM: Fourcc = Fourcc(*b"tmpo");
pub const COMMENT: Fourcc = Fourcc(*b"\xa9cmt");
pub const COMPILATION: Fourcc = Fourcc(*b"cpil");
pub const COMPOSER: Fourcc = Fourcc(*b"\xa9wrt");
pub const COPYRIGHT: Fourcc = Fourcc(*b"cprt");
pub const CUSTOM_GENRE: Fourcc = Fourcc(*b"\xa9gen");
pub const DISC_NUMBER: Fourcc = Fourcc(*b"disk");
pub const ENCODER: Fourcc = Fourcc(*b"\xa9too");
pub const STANDARD_GENRE: Fourcc = Fourcc(*b"gnre");
pub const TITLE: Fourcc = Fourcc(*b"\xa9nam");
pub const TRACK_NUMBER: Fourcc = Fourcc(*b"trkn");
pub const YEAR: Fourcc = Fourcc(*b"\xa9day");
pub const GROUPING: Fourcc = Fourcc(*b"\xa9grp");
pub const MEDIA_TYPE: Fourcc = Fourcc(*b"stik");
pub const CATEGORY: Fourcc = Fourcc(*b"catg");
pub const KEYWORD: Fourcc = Fourcc(*b"keyw");
pub const PODCAST: Fourcc = Fourcc(*b"pcst");
pub const PODCAST_EPISODE_GLOBAL_UNIQUE_ID: Fourcc = Fourcc(*b"egid");
pub const PODCAST_URL: Fourcc = Fourcc(*b"purl");
pub const DESCRIPTION: Fourcc = Fourcc(*b"desc");
pub const LYRICS: Fourcc = Fourcc(*b"\xa9lyr");
pub const TV_EPISODE: Fourcc = Fourcc(*b"tves");
pub const TV_EPISODE_NAME: Fourcc = Fourcc(*b"tven");
pub const TV_NETWORK_NAME: Fourcc = Fourcc(*b"tvnn");
pub const TV_SEASON: Fourcc = Fourcc(*b"tvsn");
pub const TV_SHOW_NAME: Fourcc = Fourcc(*b"tvsh");
pub const PURCHASE_DATE: Fourcc = Fourcc(*b"purd");
pub const GAPLESS_PLAYBACK: Fourcc = Fourcc(*b"pgap");
pub const MOVEMENT: Fourcc = Fourcc(*b"\xa9mvn");
pub const MOVEMENT_COUNT: Fourcc = Fourcc(*b"\xa9mvc");
pub const MOVEMENT_INDEX: Fourcc = Fourcc(*b"\xa9mvi");
pub const WORK: Fourcc = Fourcc(*b"\xa9wrk");
pub const SHOW_MOVEMENT: Fourcc = Fourcc(*b"shwm");
pub const ALBUM_ARTIST_SORT_ORDER: Fourcc = Fourcc(*b"soaa");
pub const ALBUM_SORT_ORDER: Fourcc = Fourcc(*b"soal");
pub const ARTIST_SORT_ORDER: Fourcc = Fourcc(*b"soar");
pub const COMPOSER_SORT_ORDER: Fourcc = Fourcc(*b"soco");
pub const TITLE_SORT_ORDER: Fourcc = Fourcc(*b"sonm");
pub const TV_SHOW_NAME_SORT_ORDER: Fourcc = Fourcc(*b"sosn");
pub const APPLE_ITUNES_MEAN: &str = "com.apple.iTunes";
pub const ISRC: FreeformIdentStatic = FreeformIdent::new_static(APPLE_ITUNES_MEAN, "ISRC");
pub const LYRICIST: FreeformIdentStatic = FreeformIdent::new_static(APPLE_ITUNES_MEAN, "LYRICIST");
pub const LABEL: FreeformIdentStatic = FreeformIdent::new_static(APPLE_ITUNES_MEAN, "LABEL");
pub trait Ident: PartialEq<DataIdent> {
fn fourcc(&self) -> Option<Fourcc>;
fn freeform(&self) -> Option<FreeformIdentBorrowed<'_>>;
}
pub fn idents_match(a: &impl Ident, b: &impl Ident) -> bool {
a.fourcc() == b.fourcc() && a.freeform() == b.freeform()
}
#[derive(Clone, Copy, Default, PartialEq, Eq)]
pub struct Fourcc(pub [u8; 4]);
impl Deref for Fourcc {
type Target = [u8; 4];
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for Fourcc {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl PartialEq<DataIdent> for Fourcc {
fn eq(&self, other: &DataIdent) -> bool {
match other {
DataIdent::Fourcc(f) => self == f,
DataIdent::Freeform { .. } => false,
}
}
}
impl Ident for Fourcc {
fn fourcc(&self) -> Option<Fourcc> {
Some(*self)
}
fn freeform(&self) -> Option<FreeformIdentBorrowed<'_>> {
None
}
}
impl FromStr for Fourcc {
type Err = TryFromSliceError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Fourcc(s.as_bytes().try_into()?))
}
}
impl fmt::Debug for Fourcc {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Fourcc(")?;
for c in self.0.iter().map(|b| char::from(*b)) {
f.write_char(c)?;
}
f.write_str(")")?;
Ok(())
}
}
impl fmt::Display for Fourcc {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for c in self.0.iter().map(|b| char::from(*b)) {
f.write_char(c)?;
}
Ok(())
}
}
pub trait StrLifetime<'a>: Clone {
type Str: AsRef<str>;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct StaticStr<'a: 'static> {
_tag: PhantomData<&'a str>,
}
impl<'a> StrLifetime<'a> for StaticStr<'a> {
type Str = &'static str;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct BorrowedStr<'a: 'a> {
_tag: PhantomData<&'a str>,
}
impl<'a> StrLifetime<'a> for BorrowedStr<'a> {
type Str = &'a str;
}
pub type FreeformIdentStatic = FreeformIdent<'static, StaticStr<'static>>;
pub type FreeformIdentBorrowed<'a> = FreeformIdent<'a, BorrowedStr<'a>>;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct FreeformIdent<'a, T> {
pub mean: &'a str,
pub name: &'a str,
_tag: PhantomData<T>,
}
impl<'a, T: StrLifetime<'a>> PartialEq<DataIdent> for FreeformIdent<'a, T> {
fn eq(&self, other: &DataIdent) -> bool {
match other {
DataIdent::Fourcc(_) => false,
DataIdent::Freeform { mean, name } => self.mean == mean && self.name == name,
}
}
}
impl<'a, T: StrLifetime<'a>> Ident for FreeformIdent<'a, T> {
fn fourcc(&self) -> Option<Fourcc> {
None
}
fn freeform(&self) -> Option<FreeformIdentBorrowed<'a>> {
Some(FreeformIdent::new_borrowed(self.mean, self.name))
}
}
impl<'a, T: StrLifetime<'a>> fmt::Display for FreeformIdent<'a, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "----:{}:{}", self.mean, self.name)
}
}
impl From<FreeformIdentStatic> for FreeformIdentBorrowed<'_> {
fn from(value: FreeformIdentStatic) -> Self {
FreeformIdent::new_borrowed(value.mean, value.name)
}
}
impl FreeformIdentStatic {
pub const fn new_static(mean: &'static str, name: &'static str) -> Self {
Self { mean, name, _tag: PhantomData }
}
}
impl<'a> FreeformIdentBorrowed<'a> {
pub const fn new_borrowed(mean: &'a str, name: &'a str) -> Self {
Self { mean, name, _tag: PhantomData }
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum DataIdent {
Fourcc(Fourcc),
Freeform {
mean: Cow<'static, str>,
name: Cow<'static, str>,
},
}
impl Ident for DataIdent {
fn fourcc(&self) -> Option<Fourcc> {
match self {
Self::Fourcc(i) => Some(*i),
Self::Freeform { .. } => None,
}
}
fn freeform(&self) -> Option<FreeformIdentBorrowed<'_>> {
match self {
Self::Fourcc(_) => None,
Self::Freeform { mean, name } => {
Some(FreeformIdent::new_borrowed(mean.as_ref(), name.as_ref()))
}
}
}
}
impl fmt::Display for DataIdent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Fourcc(ident) => write!(f, "{ident}"),
Self::Freeform { mean, name } => write!(f, "----:{mean}:{name}"),
}
}
}
impl From<Fourcc> for DataIdent {
fn from(value: Fourcc) -> Self {
Self::Fourcc(value)
}
}
impl From<FreeformIdentStatic> for DataIdent {
fn from(value: FreeformIdentStatic) -> Self {
Self::freeform(value.mean, value.name)
}
}
impl<'a> From<FreeformIdent<'a, BorrowedStr<'a>>> for DataIdent {
fn from(value: FreeformIdent<'a, BorrowedStr<'a>>) -> Self {
Self::freeform(value.mean.to_owned(), value.name.to_owned())
}
}
impl DataIdent {
pub fn freeform(
mean: impl Into<Cow<'static, str>>,
name: impl Into<Cow<'static, str>>,
) -> Self {
Self::Freeform { mean: mean.into(), name: name.into() }
}
pub const fn fourcc(bytes: [u8; 4]) -> Self {
Self::Fourcc(Fourcc(bytes))
}
}