#![feature(step_trait)]
use std::fmt;
use std::iter;
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,
};
#[derive(Debug)]
pub struct TimecodeError {
pub kind: TimecodeErrorKind,
}
#[derive(Debug)]
pub enum TimecodeErrorKind {
InvalidFormat,
InvalidTimecode,
}
#[derive(Clone, PartialEq, PartialOrd)]
pub struct Timecode<T: FrameRate> {
pub frame_number: u32,
pub hour: u8,
pub minute: u8,
pub second: u8,
pub frame: u8,
frame_rate: marker::PhantomData<T>,
}
impl<T: FrameRate> Timecode<T> {
pub fn new(hour: u8, minute: u8, second: u8, frame: u8) -> Result<Timecode<T>, TimecodeError> {
use self::TimecodeErrorKind::*;
let result = T::calculate_frame_number(
u32::from(hour),
u32::from(minute),
u32::from(second),
u32::from(frame),
);
match result {
Some(frame_number) => Ok(Timecode {
frame_number,
hour,
minute,
second,
frame,
frame_rate: marker::PhantomData,
}),
None => Err(TimecodeError {
kind: InvalidTimecode,
}),
}
}
}
impl<T: FrameRate> str::FromStr for Timecode<T> {
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 = it.by_ref().take(2).collect::<String>();
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 = it.by_ref().take(2).collect::<String>();
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 = it.by_ref().take(2).collect::<String>();
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 = it.by_ref().take(2).collect::<String>();
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: FrameRate> fmt::Display for Timecode<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let separator = if T::DROP_FRAME { ';' } else { ':' };
write!(
f,
"{:02}:{:02}:{:02}{}{:02}",
self.hour, self.minute, self.second, separator, self.frame
)
}
}
impl<T: FrameRate> fmt::Debug for Timecode<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let separator = if T::DROP_FRAME { ';' } else { ':' };
write!(
f,
"Timecode {{ timecode: {:02}:{:02}:{:02}{}{:02}, frame_number: {}, frame_rate: {} }}",
self.hour,
self.minute,
self.second,
separator,
self.frame,
self.frame_number,
T::DISPLAY,
)
}
}
impl<T: FrameRate> From<u32> for Timecode<T> {
fn from(frame_number: u32) -> Self {
let new_frame_number = frame_number % T::MAX_FRAMES;
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: FrameRate> ops::Add<u32> for Timecode<T> {
type Output = Self;
fn add(self, other: u32) -> Self {
Timecode::<T>::from(self.frame_number + other)
}
}
impl<T: FrameRate> ops::AddAssign<u32> for Timecode<T> {
fn add_assign(&mut self, other: u32) {
let new_frame_number = (self.frame_number + other) % T::MAX_FRAMES;
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: FrameRate> ops::Sub<u32> for Timecode<T> {
type Output = Self;
fn sub(self, other: u32) -> Self {
Timecode::<T>::from(self.frame_number - other)
}
}
impl<T: FrameRate> ops::SubAssign<u32> for Timecode<T> {
fn sub_assign(&mut self, other: u32) {
let new_frame_number = (self.frame_number - other) % T::MAX_FRAMES;
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: FrameRate> ops::Add for Timecode<T> {
type Output = Timecode<T>;
fn add(self, other: Self) -> Self {
self + other.frame_number
}
}
impl<T: FrameRate> ops::AddAssign for Timecode<T> {
fn add_assign(&mut self, other: Self) {
*self += other.frame_number;
}
}
impl<T: FrameRate + Clone + PartialOrd> iter::Step for Timecode<T> {
fn steps_between(start: &Self, end: &Self) -> Option<usize> {
if start.frame_number <= end.frame_number {
Some((end.frame_number - start.frame_number) as usize)
} else {
None
}
}
fn replace_one(&mut self) -> Self {
std::mem::replace(
self,
Timecode {
frame_number: 1,
hour: 0,
minute: 0,
second: 0,
frame: 1,
frame_rate: marker::PhantomData,
},
)
}
fn replace_zero(&mut self) -> Self {
std::mem::replace(
self,
Timecode {
frame_number: 0,
hour: 0,
minute: 0,
second: 0,
frame: 0,
frame_rate: marker::PhantomData,
},
)
}
fn add_one(&self) -> Self {
if u32::from(self.frame) < (T::FPS - 1) {
Timecode {
frame_number: self.frame_number + 1,
hour: self.hour,
minute: self.minute,
second: self.second,
frame: self.frame + 1,
frame_rate: marker::PhantomData,
}
} else if self.second < 59 {
Timecode {
frame_number: self.frame_number + 1,
hour: self.hour,
minute: self.minute,
second: self.second + 1,
frame: 0,
frame_rate: marker::PhantomData,
}
} else if self.minute < 59 {
let new_minute = self.minute + 1;
let new_frame = if T::DROP_FRAME && (new_minute % 10 != 0) {
T::DROP_FRAME_COUNT as u8
} else {
0
};
Timecode {
frame_number: self.frame_number + 1,
hour: self.hour,
minute: new_minute,
second: 0,
frame: new_frame,
frame_rate: marker::PhantomData,
}
} else {
Timecode {
frame_number: self.frame_number + 1,
hour: self.hour + 1,
minute: 0,
second: 0,
frame: 0,
frame_rate: marker::PhantomData,
}
}
}
fn sub_one(&self) -> Self {
if u32::from(self.frame) > 0 {
Timecode {
frame_number: self.frame_number - 1,
hour: self.hour,
minute: self.minute,
second: self.second,
frame: self.frame - 1,
frame_rate: marker::PhantomData,
}
} else if self.second > 0 {
Timecode {
frame_number: self.frame_number - 1,
hour: self.hour,
minute: self.minute,
second: self.second - 1,
frame: T::FPS as u8 - 1,
frame_rate: marker::PhantomData,
}
} else if self.minute > 0 {
Timecode {
frame_number: self.frame_number - 1,
hour: self.hour,
minute: self.minute - 1,
second: 59,
frame: T::FPS as u8 - 1,
frame_rate: marker::PhantomData,
}
} else {
Timecode {
frame_number: self.frame_number - 1,
hour: self.hour - 1,
minute: 59,
second: 59,
frame: T::FPS as u8 - 1,
frame_rate: marker::PhantomData,
}
}
}
fn add_usize(&self, n: usize) -> Option<Self> {
if n == 1 {
Some(self.add_one())
} else {
Some(self.clone() + (n as u32))
}
}
}