use serde::Serialize;
use std::borrow::Cow;
use std::convert::TryFrom;
use std::fmt;
use crate::mp4box::*;
use crate::*;
pub use bytes::Bytes;
pub use num_rational::Ratio;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
pub struct FixedPointU8(Ratio<u16>);
impl FixedPointU8 {
pub fn new(val: u8) -> Self {
Self(Ratio::new_raw(val as u16 * 0x100, 0x100))
}
pub fn new_raw(val: u16) -> Self {
Self(Ratio::new_raw(val, 0x100))
}
pub fn value(&self) -> u8 {
self.0.to_integer() as u8
}
pub fn raw_value(&self) -> u16 {
*self.0.numer()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
pub struct FixedPointI8(Ratio<i16>);
impl FixedPointI8 {
pub fn new(val: i8) -> Self {
Self(Ratio::new_raw(val as i16 * 0x100, 0x100))
}
pub fn new_raw(val: i16) -> Self {
Self(Ratio::new_raw(val, 0x100))
}
pub fn value(&self) -> i8 {
self.0.to_integer() as i8
}
pub fn raw_value(&self) -> i16 {
*self.0.numer()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
pub struct FixedPointU16(Ratio<u32>);
impl FixedPointU16 {
pub fn new(val: u16) -> Self {
Self(Ratio::new_raw(val as u32 * 0x10000, 0x10000))
}
pub fn new_raw(val: u32) -> Self {
Self(Ratio::new_raw(val, 0x10000))
}
pub fn value(&self) -> u16 {
self.0.to_integer() as u16
}
pub fn raw_value(&self) -> u32 {
*self.0.numer()
}
}
impl fmt::Debug for BoxType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let fourcc: FourCC = From::from(*self);
write!(f, "{fourcc}")
}
}
impl fmt::Display for BoxType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let fourcc: FourCC = From::from(*self);
write!(f, "{fourcc}")
}
}
#[derive(Default, PartialEq, Eq, Clone, Copy, Serialize)]
pub struct FourCC {
pub value: [u8; 4],
}
impl std::str::FromStr for FourCC {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
if let [a, b, c, d] = s.as_bytes() {
Ok(Self {
value: [*a, *b, *c, *d],
})
} else {
Err(Error::InvalidData("expected exactly four bytes in string"))
}
}
}
impl From<u32> for FourCC {
fn from(number: u32) -> Self {
FourCC {
value: number.to_be_bytes(),
}
}
}
impl From<FourCC> for u32 {
fn from(fourcc: FourCC) -> u32 {
(&fourcc).into()
}
}
impl From<&FourCC> for u32 {
fn from(fourcc: &FourCC) -> u32 {
u32::from_be_bytes(fourcc.value)
}
}
impl From<[u8; 4]> for FourCC {
fn from(value: [u8; 4]) -> FourCC {
FourCC { value }
}
}
impl From<BoxType> for FourCC {
fn from(t: BoxType) -> FourCC {
let box_num: u32 = Into::into(t);
From::from(box_num)
}
}
impl fmt::Debug for FourCC {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let code: u32 = self.into();
let string = String::from_utf8_lossy(&self.value[..]);
write!(f, "{string} / {code:#010X}")
}
}
impl fmt::Display for FourCC {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", String::from_utf8_lossy(&self.value[..]))
}
}
const DISPLAY_TYPE_VIDEO: &str = "Video";
const DISPLAY_TYPE_AUDIO: &str = "Audio";
const DISPLAY_TYPE_SUBTITLE: &str = "Subtitle";
const HANDLER_TYPE_VIDEO: &str = "vide";
const HANDLER_TYPE_VIDEO_FOURCC: [u8; 4] = [b'v', b'i', b'd', b'e'];
const HANDLER_TYPE_AUDIO: &str = "soun";
const HANDLER_TYPE_AUDIO_FOURCC: [u8; 4] = [b's', b'o', b'u', b'n'];
const HANDLER_TYPE_SUBTITLE: &str = "sbtl";
const HANDLER_TYPE_SUBTITLE_FOURCC: [u8; 4] = [b's', b'b', b't', b'l'];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TrackType {
Video,
Audio,
Subtitle,
}
impl fmt::Display for TrackType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s = match self {
TrackType::Video => DISPLAY_TYPE_VIDEO,
TrackType::Audio => DISPLAY_TYPE_AUDIO,
TrackType::Subtitle => DISPLAY_TYPE_SUBTITLE,
};
write!(f, "{s}")
}
}
impl TryFrom<&str> for TrackType {
type Error = Error;
fn try_from(handler: &str) -> Result<TrackType> {
match handler {
HANDLER_TYPE_VIDEO => Ok(TrackType::Video),
HANDLER_TYPE_AUDIO => Ok(TrackType::Audio),
HANDLER_TYPE_SUBTITLE => Ok(TrackType::Subtitle),
_ => Err(Error::InvalidData("unsupported handler type")),
}
}
}
impl TryFrom<&FourCC> for TrackType {
type Error = Error;
fn try_from(fourcc: &FourCC) -> Result<TrackType> {
match fourcc.value {
HANDLER_TYPE_VIDEO_FOURCC => Ok(TrackType::Video),
HANDLER_TYPE_AUDIO_FOURCC => Ok(TrackType::Audio),
HANDLER_TYPE_SUBTITLE_FOURCC => Ok(TrackType::Subtitle),
_ => Err(Error::InvalidData("unsupported handler type")),
}
}
}
impl From<TrackType> for FourCC {
fn from(t: TrackType) -> FourCC {
match t {
TrackType::Video => HANDLER_TYPE_VIDEO_FOURCC.into(),
TrackType::Audio => HANDLER_TYPE_AUDIO_FOURCC.into(),
TrackType::Subtitle => HANDLER_TYPE_SUBTITLE_FOURCC.into(),
}
}
}
const MEDIA_TYPE_H264: &str = "h264";
const MEDIA_TYPE_H265: &str = "h265";
const MEDIA_TYPE_VP9: &str = "vp9";
const MEDIA_TYPE_AAC: &str = "aac";
const MEDIA_TYPE_TTXT: &str = "ttxt";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MediaType {
H264,
H265,
VP9,
AAC,
TTXT,
}
impl fmt::Display for MediaType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s: &str = self.into();
write!(f, "{s}")
}
}
impl TryFrom<&str> for MediaType {
type Error = Error;
fn try_from(media: &str) -> Result<MediaType> {
match media {
MEDIA_TYPE_H264 => Ok(MediaType::H264),
MEDIA_TYPE_H265 => Ok(MediaType::H265),
MEDIA_TYPE_VP9 => Ok(MediaType::VP9),
MEDIA_TYPE_AAC => Ok(MediaType::AAC),
MEDIA_TYPE_TTXT => Ok(MediaType::TTXT),
_ => Err(Error::InvalidData("unsupported media type")),
}
}
}
impl From<MediaType> for &str {
fn from(t: MediaType) -> &'static str {
match t {
MediaType::H264 => MEDIA_TYPE_H264,
MediaType::H265 => MEDIA_TYPE_H265,
MediaType::VP9 => MEDIA_TYPE_VP9,
MediaType::AAC => MEDIA_TYPE_AAC,
MediaType::TTXT => MEDIA_TYPE_TTXT,
}
}
}
impl From<&MediaType> for &str {
fn from(t: &MediaType) -> &'static str {
match t {
MediaType::H264 => MEDIA_TYPE_H264,
MediaType::H265 => MEDIA_TYPE_H265,
MediaType::VP9 => MEDIA_TYPE_VP9,
MediaType::AAC => MEDIA_TYPE_AAC,
MediaType::TTXT => MEDIA_TYPE_TTXT,
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum AvcProfile {
AvcConstrainedBaseline, AvcBaseline, AvcMain, AvcExtended, AvcHigh, }
impl TryFrom<(u8, u8)> for AvcProfile {
type Error = Error;
fn try_from(value: (u8, u8)) -> Result<AvcProfile> {
let profile = value.0;
let constraint_set1_flag = (value.1 & 0x40) >> 7;
match (profile, constraint_set1_flag) {
(66, 1) => Ok(AvcProfile::AvcConstrainedBaseline),
(66, 0) => Ok(AvcProfile::AvcBaseline),
(77, _) => Ok(AvcProfile::AvcMain),
(88, _) => Ok(AvcProfile::AvcExtended),
(100, _) => Ok(AvcProfile::AvcHigh),
_ => Err(Error::InvalidData("unsupported avc profile")),
}
}
}
impl fmt::Display for AvcProfile {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let profile = match self {
AvcProfile::AvcConstrainedBaseline => "Constrained Baseline",
AvcProfile::AvcBaseline => "Baseline",
AvcProfile::AvcMain => "Main",
AvcProfile::AvcExtended => "Extended",
AvcProfile::AvcHigh => "High",
};
write!(f, "{profile}")
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum AudioObjectType {
AacMain = 1, AacLowComplexity = 2, AacScalableSampleRate = 3, AacLongTermPrediction = 4, SpectralBandReplication = 5, AACScalable = 6, TwinVQ = 7, CodeExcitedLinearPrediction = 8, HarmonicVectorExcitationCoding = 9, TextToSpeechtInterface = 12, MainSynthetic = 13, WavetableSynthesis = 14, GeneralMIDI = 15, AlgorithmicSynthesis = 16, ErrorResilientAacLowComplexity = 17, ErrorResilientAacLongTermPrediction = 19, ErrorResilientAacScalable = 20, ErrorResilientAacTwinVQ = 21, ErrorResilientAacBitSlicedArithmeticCoding = 22, ErrorResilientAacLowDelay = 23, ErrorResilientCodeExcitedLinearPrediction = 24, ErrorResilientHarmonicVectorExcitationCoding = 25, ErrorResilientHarmonicIndividualLinesNoise = 26, ErrorResilientParametric = 27, SinuSoidalCoding = 28, ParametricStereo = 29, MpegSurround = 30, MpegLayer1 = 32, MpegLayer2 = 33, MpegLayer3 = 34, DirectStreamTransfer = 35, AudioLosslessCoding = 36, ScalableLosslessCoding = 37, ScalableLosslessCodingNoneCore = 38, ErrorResilientAacEnhancedLowDelay = 39, SymbolicMusicRepresentationSimple = 40, SymbolicMusicRepresentationMain = 41, UnifiedSpeechAudioCoding = 42, SpatialAudioObjectCoding = 43, LowDelayMpegSurround = 44, SpatialAudioObjectCodingDialogueEnhancement = 45, AudioSync = 46, }
impl TryFrom<u8> for AudioObjectType {
type Error = Error;
fn try_from(value: u8) -> Result<AudioObjectType> {
match value {
1 => Ok(AudioObjectType::AacMain),
2 => Ok(AudioObjectType::AacLowComplexity),
3 => Ok(AudioObjectType::AacScalableSampleRate),
4 => Ok(AudioObjectType::AacLongTermPrediction),
5 => Ok(AudioObjectType::SpectralBandReplication),
6 => Ok(AudioObjectType::AACScalable),
7 => Ok(AudioObjectType::TwinVQ),
8 => Ok(AudioObjectType::CodeExcitedLinearPrediction),
9 => Ok(AudioObjectType::HarmonicVectorExcitationCoding),
12 => Ok(AudioObjectType::TextToSpeechtInterface),
13 => Ok(AudioObjectType::MainSynthetic),
14 => Ok(AudioObjectType::WavetableSynthesis),
15 => Ok(AudioObjectType::GeneralMIDI),
16 => Ok(AudioObjectType::AlgorithmicSynthesis),
17 => Ok(AudioObjectType::ErrorResilientAacLowComplexity),
19 => Ok(AudioObjectType::ErrorResilientAacLongTermPrediction),
20 => Ok(AudioObjectType::ErrorResilientAacScalable),
21 => Ok(AudioObjectType::ErrorResilientAacTwinVQ),
22 => Ok(AudioObjectType::ErrorResilientAacBitSlicedArithmeticCoding),
23 => Ok(AudioObjectType::ErrorResilientAacLowDelay),
24 => Ok(AudioObjectType::ErrorResilientCodeExcitedLinearPrediction),
25 => Ok(AudioObjectType::ErrorResilientHarmonicVectorExcitationCoding),
26 => Ok(AudioObjectType::ErrorResilientHarmonicIndividualLinesNoise),
27 => Ok(AudioObjectType::ErrorResilientParametric),
28 => Ok(AudioObjectType::SinuSoidalCoding),
29 => Ok(AudioObjectType::ParametricStereo),
30 => Ok(AudioObjectType::MpegSurround),
32 => Ok(AudioObjectType::MpegLayer1),
33 => Ok(AudioObjectType::MpegLayer2),
34 => Ok(AudioObjectType::MpegLayer3),
35 => Ok(AudioObjectType::DirectStreamTransfer),
36 => Ok(AudioObjectType::AudioLosslessCoding),
37 => Ok(AudioObjectType::ScalableLosslessCoding),
38 => Ok(AudioObjectType::ScalableLosslessCodingNoneCore),
39 => Ok(AudioObjectType::ErrorResilientAacEnhancedLowDelay),
40 => Ok(AudioObjectType::SymbolicMusicRepresentationSimple),
41 => Ok(AudioObjectType::SymbolicMusicRepresentationMain),
42 => Ok(AudioObjectType::UnifiedSpeechAudioCoding),
43 => Ok(AudioObjectType::SpatialAudioObjectCoding),
44 => Ok(AudioObjectType::LowDelayMpegSurround),
45 => Ok(AudioObjectType::SpatialAudioObjectCodingDialogueEnhancement),
46 => Ok(AudioObjectType::AudioSync),
_ => Err(Error::InvalidData("invalid audio object type")),
}
}
}
impl fmt::Display for AudioObjectType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let type_str = match self {
AudioObjectType::AacMain => "AAC Main",
AudioObjectType::AacLowComplexity => "LC",
AudioObjectType::AacScalableSampleRate => "SSR",
AudioObjectType::AacLongTermPrediction => "LTP",
AudioObjectType::SpectralBandReplication => "SBR",
AudioObjectType::AACScalable => "Scalable",
AudioObjectType::TwinVQ => "TwinVQ",
AudioObjectType::CodeExcitedLinearPrediction => "CELP",
AudioObjectType::HarmonicVectorExcitationCoding => "HVXC",
AudioObjectType::TextToSpeechtInterface => "TTSI",
AudioObjectType::MainSynthetic => "Main Synthetic",
AudioObjectType::WavetableSynthesis => "Wavetable Synthesis",
AudioObjectType::GeneralMIDI => "General MIDI",
AudioObjectType::AlgorithmicSynthesis => "Algorithmic Synthesis",
AudioObjectType::ErrorResilientAacLowComplexity => "ER AAC LC",
AudioObjectType::ErrorResilientAacLongTermPrediction => "ER AAC LTP",
AudioObjectType::ErrorResilientAacScalable => "ER AAC scalable",
AudioObjectType::ErrorResilientAacTwinVQ => "ER AAC TwinVQ",
AudioObjectType::ErrorResilientAacBitSlicedArithmeticCoding => "ER AAC BSAC",
AudioObjectType::ErrorResilientAacLowDelay => "ER AAC LD",
AudioObjectType::ErrorResilientCodeExcitedLinearPrediction => "ER CELP",
AudioObjectType::ErrorResilientHarmonicVectorExcitationCoding => "ER HVXC",
AudioObjectType::ErrorResilientHarmonicIndividualLinesNoise => "ER HILN",
AudioObjectType::ErrorResilientParametric => "ER Parametric",
AudioObjectType::SinuSoidalCoding => "SSC",
AudioObjectType::ParametricStereo => "Parametric Stereo",
AudioObjectType::MpegSurround => "MPEG surround",
AudioObjectType::MpegLayer1 => "MPEG Layer 1",
AudioObjectType::MpegLayer2 => "MPEG Layer 2",
AudioObjectType::MpegLayer3 => "MPEG Layer 3",
AudioObjectType::DirectStreamTransfer => "DST",
AudioObjectType::AudioLosslessCoding => "ALS",
AudioObjectType::ScalableLosslessCoding => "SLS",
AudioObjectType::ScalableLosslessCodingNoneCore => "SLS Non-core",
AudioObjectType::ErrorResilientAacEnhancedLowDelay => "ER AAC ELD",
AudioObjectType::SymbolicMusicRepresentationSimple => "SMR Simple",
AudioObjectType::SymbolicMusicRepresentationMain => "SMR Main",
AudioObjectType::UnifiedSpeechAudioCoding => "USAC",
AudioObjectType::SpatialAudioObjectCoding => "SAOC",
AudioObjectType::LowDelayMpegSurround => "LD MPEG Surround",
AudioObjectType::SpatialAudioObjectCodingDialogueEnhancement => "SAOC-DE",
AudioObjectType::AudioSync => "Audio Sync",
};
write!(f, "{type_str}")
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum SampleFreqIndex {
Freq96000 = 0x0,
Freq88200 = 0x1,
Freq64000 = 0x2,
Freq48000 = 0x3,
Freq44100 = 0x4,
Freq32000 = 0x5,
Freq24000 = 0x6,
Freq22050 = 0x7,
Freq16000 = 0x8,
Freq12000 = 0x9,
Freq11025 = 0xa,
Freq8000 = 0xb,
Freq7350 = 0xc,
}
impl TryFrom<u8> for SampleFreqIndex {
type Error = Error;
fn try_from(value: u8) -> Result<SampleFreqIndex> {
match value {
0x0 => Ok(SampleFreqIndex::Freq96000),
0x1 => Ok(SampleFreqIndex::Freq88200),
0x2 => Ok(SampleFreqIndex::Freq64000),
0x3 => Ok(SampleFreqIndex::Freq48000),
0x4 => Ok(SampleFreqIndex::Freq44100),
0x5 => Ok(SampleFreqIndex::Freq32000),
0x6 => Ok(SampleFreqIndex::Freq24000),
0x7 => Ok(SampleFreqIndex::Freq22050),
0x8 => Ok(SampleFreqIndex::Freq16000),
0x9 => Ok(SampleFreqIndex::Freq12000),
0xa => Ok(SampleFreqIndex::Freq11025),
0xb => Ok(SampleFreqIndex::Freq8000),
0xc => Ok(SampleFreqIndex::Freq7350),
_ => Err(Error::InvalidData("invalid sampling frequency index")),
}
}
}
impl SampleFreqIndex {
pub fn freq(&self) -> u32 {
match *self {
SampleFreqIndex::Freq96000 => 96000,
SampleFreqIndex::Freq88200 => 88200,
SampleFreqIndex::Freq64000 => 64000,
SampleFreqIndex::Freq48000 => 48000,
SampleFreqIndex::Freq44100 => 44100,
SampleFreqIndex::Freq32000 => 32000,
SampleFreqIndex::Freq24000 => 24000,
SampleFreqIndex::Freq22050 => 22050,
SampleFreqIndex::Freq16000 => 16000,
SampleFreqIndex::Freq12000 => 12000,
SampleFreqIndex::Freq11025 => 11025,
SampleFreqIndex::Freq8000 => 8000,
SampleFreqIndex::Freq7350 => 7350,
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum ChannelConfig {
Mono = 0x1,
Stereo = 0x2,
Three = 0x3,
Four = 0x4,
Five = 0x5,
FiveOne = 0x6,
SevenOne = 0x7,
}
impl TryFrom<u8> for ChannelConfig {
type Error = Error;
fn try_from(value: u8) -> Result<ChannelConfig> {
match value {
0x1 => Ok(ChannelConfig::Mono),
0x2 => Ok(ChannelConfig::Stereo),
0x3 => Ok(ChannelConfig::Three),
0x4 => Ok(ChannelConfig::Four),
0x5 => Ok(ChannelConfig::Five),
0x6 => Ok(ChannelConfig::FiveOne),
0x7 => Ok(ChannelConfig::SevenOne),
_ => Err(Error::InvalidData("invalid channel configuration")),
}
}
}
impl fmt::Display for ChannelConfig {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s = match self {
ChannelConfig::Mono => "mono",
ChannelConfig::Stereo => "stereo",
ChannelConfig::Three => "three",
ChannelConfig::Four => "four",
ChannelConfig::Five => "five",
ChannelConfig::FiveOne => "five.one",
ChannelConfig::SevenOne => "seven.one",
};
write!(f, "{s}")
}
}
#[derive(Debug, PartialEq, Eq, Clone, Default)]
pub struct AvcConfig {
pub width: u16,
pub height: u16,
pub seq_param_set: Vec<u8>,
pub pic_param_set: Vec<u8>,
}
#[derive(Debug, PartialEq, Eq, Clone, Default)]
pub struct HevcConfig {
pub width: u16,
pub height: u16,
}
#[derive(Debug, PartialEq, Eq, Clone, Default)]
pub struct Vp9Config {
pub width: u16,
pub height: u16,
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct AacConfig {
pub bitrate: u32,
pub profile: AudioObjectType,
pub freq_index: SampleFreqIndex,
pub chan_conf: ChannelConfig,
}
impl Default for AacConfig {
fn default() -> Self {
Self {
bitrate: 0,
profile: AudioObjectType::AacLowComplexity,
freq_index: SampleFreqIndex::Freq48000,
chan_conf: ChannelConfig::Stereo,
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Default)]
pub struct TtxtConfig {}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum MediaConfig {
AvcConfig(AvcConfig),
HevcConfig(HevcConfig),
Vp9Config(Vp9Config),
AacConfig(AacConfig),
TtxtConfig(TtxtConfig),
}
#[derive(Debug)]
pub struct Mp4Sample {
pub start_time: u64,
pub duration: u32,
pub rendering_offset: i32,
pub is_sync: bool,
pub bytes: Bytes,
}
impl PartialEq for Mp4Sample {
fn eq(&self, other: &Self) -> bool {
self.start_time == other.start_time
&& self.duration == other.duration
&& self.rendering_offset == other.rendering_offset
&& self.is_sync == other.is_sync
&& self.bytes.len() == other.bytes.len() }
}
impl fmt::Display for Mp4Sample {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"start_time {}, duration {}, rendering_offset {}, is_sync {}, length {}",
self.start_time,
self.duration,
self.rendering_offset,
self.is_sync,
self.bytes.len()
)
}
}
pub fn creation_time(creation_time: u64) -> u64 {
if creation_time >= 2082844800 {
creation_time - 2082844800
} else {
creation_time
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub enum DataType {
Binary = 0x000000,
Text = 0x000001,
Image = 0x00000D,
TempoCpil = 0x000015,
}
#[allow(clippy::derivable_impls)]
impl std::default::Default for DataType {
fn default() -> Self {
DataType::Binary
}
}
impl TryFrom<u32> for DataType {
type Error = Error;
fn try_from(value: u32) -> Result<DataType> {
match value {
0x000000 => Ok(DataType::Binary),
0x000001 => Ok(DataType::Text),
0x00000D => Ok(DataType::Image),
0x000015 => Ok(DataType::TempoCpil),
_ => Err(Error::InvalidData("invalid data type")),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
pub enum MetadataKey {
Title,
Year,
Poster,
Summary,
}
pub trait Metadata<'a> {
fn title(&self) -> Option<Cow<str>>;
fn year(&self) -> Option<u32>;
fn poster(&self) -> Option<&[u8]>;
fn summary(&self) -> Option<Cow<str>>;
}
impl<'a, T: Metadata<'a>> Metadata<'a> for &'a T {
fn title(&self) -> Option<Cow<str>> {
(**self).title()
}
fn year(&self) -> Option<u32> {
(**self).year()
}
fn poster(&self) -> Option<&[u8]> {
(**self).poster()
}
fn summary(&self) -> Option<Cow<str>> {
(**self).summary()
}
}
impl<'a, T: Metadata<'a>> Metadata<'a> for Option<T> {
fn title(&self) -> Option<Cow<str>> {
self.as_ref().and_then(|t| t.title())
}
fn year(&self) -> Option<u32> {
self.as_ref().and_then(|t| t.year())
}
fn poster(&self) -> Option<&[u8]> {
self.as_ref().and_then(|t| t.poster())
}
fn summary(&self) -> Option<Cow<str>> {
self.as_ref().and_then(|t| t.summary())
}
}