use std::fmt;
use std::marker;
use std::ops;
use std::str;
mod frame_rate;
pub use frame_rate::{FrameRate, FrameRate2398, FrameRate24, FrameRate25,
FrameRate2997, FrameRate30, FrameRate50, FrameRate5994,
FrameRate60};
use frame_rate::NormalizeFrameNumber;
#[derive(Debug)]
pub struct TimecodeError {
pub kind: TimecodeErrorKind,
}
#[derive(Debug)]
pub enum TimecodeErrorKind {
InvalidFormat,
InvalidTimecode
}
#[derive(Debug, PartialEq)]
pub struct Timecode<FrameRate> {
pub frame_number: u32,
pub hour: u8,
pub minute: u8,
pub second: u8,
pub frame: u8,
frame_rate: marker::PhantomData<FrameRate>,
}
impl<T> Timecode<T> {
pub fn new(
hour: u8,
minute: u8,
second: u8,
frame: u8,
) -> Result<Timecode<T>, TimecodeError>
where
T: FrameRate,
{
use self::TimecodeErrorKind::*;
let result = T::calculate_frame_number(
hour as u32,
minute as u32,
second as u32,
frame as u32,
);
match result {
Some(frame_number) => Ok(Timecode {
frame_number,
hour,
minute,
second,
frame,
frame_rate: marker::PhantomData,
}),
None => Err(TimecodeError {
kind: InvalidTimecode,
}),
}
}
}
impl<T> str::FromStr for Timecode<T>
where
T: FrameRate,
{
type Err = TimecodeError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
use self::TimecodeErrorKind::*;
let mut colon_notation = false;
let mut semi_colon_notation = false;
let mut dot_notation = false;
let mut it = s.chars();
let hour_string: String = it.by_ref().take(2).collect();
let hour: u8 = match hour_string.parse() {
Ok(n) if n < 60 => n,
_ => {
return Err(TimecodeError {
kind: InvalidFormat,
});
}
};
let minute_sep_char = it.next();
match minute_sep_char {
Some(':') => colon_notation = true,
Some(';') => semi_colon_notation = true,
Some('.') => dot_notation = true,
_ => {
return Err(TimecodeError {
kind: InvalidFormat,
});
}
};
let minute_string: String = it.by_ref().take(2).collect();
let minute: u8 = match minute_string.parse() {
Ok(n) if n < 60 => n,
_ => {
return Err(TimecodeError {
kind: InvalidFormat,
});
}
};
let second_sep_char = it.next();
match second_sep_char {
Some(':') if colon_notation => {}
Some(';') if semi_colon_notation => {}
Some('.') if dot_notation => {}
_ => {
return Err(TimecodeError {
kind: InvalidFormat,
});
}
}
let second_string: String = it.by_ref().take(2).collect();
let second: u8 = match second_string.parse() {
Ok(n) if n < 60 => n,
_ => {
return Err(TimecodeError {
kind: InvalidFormat,
});
}
};
let frame_sep_char = it.next();
let drop_frame = match frame_sep_char {
Some(':') if colon_notation => false,
Some(';') if semi_colon_notation || colon_notation => true,
Some('.') if dot_notation || colon_notation => true,
_ => {
return Err(TimecodeError {
kind: InvalidFormat,
});
}
};
let frame_string: String = it.by_ref().take(2).collect();
let frame: u8 = match frame_string.parse() {
Ok(n) => n,
_ => {
return Err(TimecodeError {
kind: InvalidFormat,
});
}
};
if it.next() != None {
return Err(TimecodeError {
kind: InvalidFormat,
});
}
if drop_frame && !T::DROP_FRAME {
return Err(TimecodeError {
kind: InvalidFormat,
});
}
match Timecode::<T>::new(hour, minute, second, frame) {
Ok(timecode) => Ok(timecode),
Err(_) => Err(TimecodeError {
kind: InvalidTimecode,
}),
}
}
}
impl<T> fmt::Display for Timecode<T>
where
T: FrameRate,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let separator = match T::DROP_FRAME {
true => ';',
false => ':',
};
write!(
f,
"{:02}:{:02}:{:02}{}{:02}",
self.hour, self.minute, self.second, separator, self.frame
)
}
}
macro_rules! impl_int_all {
($($t:ty)*) => ($(
impl<T> From<$t> for Timecode<T>
where
T: FrameRate,
{
fn from(frame_number: $t) -> Self {
let new_frame_number = frame_number.normalize(T::MAX_FRAMES as $t);
let (hour, minute, second, frame) =
T::calculate_time_code(new_frame_number);
Timecode {
frame_number: new_frame_number,
hour,
minute,
second,
frame,
frame_rate: marker::PhantomData,
}
}
}
impl<T> ops::Add<$t> for Timecode<T>
where
T: FrameRate,
{
type Output = Self;
fn add(self, other: $t) -> Self {
Timecode::<T>::from(self.frame_number as $t + other)
}
}
impl<T> ops::AddAssign<$t> for Timecode<T>
where
T: FrameRate,
{
fn add_assign(&mut self, other: $t) {
let new_frame_number = (self.frame_number as $t + other)
.normalize(T::MAX_FRAMES as $t);
let (hour, minute, second, frame) =
T::calculate_time_code(new_frame_number);
self.hour = hour;
self.minute = minute;
self.second = second;
self.frame = frame;
self.frame_number = new_frame_number;
}
}
impl<T> ops::Sub<$t> for Timecode<T>
where
T: FrameRate,
{
type Output = Self;
fn sub(self, other: $t) -> Self {
Timecode::<T>::from(self.frame_number as $t - other)
}
}
impl<T> ops::SubAssign<$t> for Timecode<T>
where
T: FrameRate,
{
fn sub_assign(&mut self, other: $t) {
let new_frame_number = (self.frame_number as $t - other)
.normalize(T::MAX_FRAMES as $t);
let (hour, minute, second, frame) =
T::calculate_time_code(new_frame_number);
self.hour = hour;
self.minute = minute;
self.second = second;
self.frame = frame;
self.frame_number = new_frame_number;
}
}
)*)
}
impl_int_all! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 }
impl<T> ops::Add for Timecode<T>
where
T: FrameRate,
{
type Output = Timecode<T>;
fn add(self, other: Self) -> Self {
self + other.frame_number
}
}
impl<T> ops::AddAssign for Timecode<T>
where
T: FrameRate,
{
fn add_assign(&mut self, other: Self) {
*self += other.frame_number;
}
}