use errors::Error;
use std::cmp::Ordering;
use std::fmt;
use std::ops::Sub;
use std::str::FromStr;
mod tokenization;
use self::tokenization::tokenize;
pub use self::tokenization::Token;
mod command;
pub use self::command::Command;
const FPS: i64 = 75;
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct Time {
mins: i32,
secs: i8,
frames: i8,
}
impl Time {
pub fn new(minutes: i32, seconds: i8, frames: i8) -> Time {
Time {
mins: minutes,
secs: seconds,
frames: frames,
}
}
pub fn to_string_2(&self) -> String {
format!("{:02}:{:02}", self.mins, self.secs)
}
pub fn minutes(&self) -> i32 {
self.mins
}
pub fn seconds(&self) -> i8 {
self.secs
}
pub fn frames(&self) -> i8 {
self.frames
}
pub fn total_minutes(&self) -> f64 {
self.total_seconds() / 60.
}
pub fn total_seconds(&self) -> f64 {
self.mins as f64 * 60. + self.secs as f64 + (self.frames as f64) / (FPS as f64)
}
pub fn total_frames(&self) -> i64 {
(self.mins as i64 * 60 + self.secs as i64) * FPS + self.frames as i64
}
pub fn from_frames(from: i64) -> Time {
let frames = from % FPS;
let secs_all = from / FPS;
let secs = secs_all % 60;
let mins = secs_all / 60;
Time {
mins: mins as i32,
secs: secs as i8,
frames: frames as i8,
}
}
}
impl Ord for Time {
fn cmp(&self, other: &Time) -> Ordering {
self.total_frames().cmp(&other.total_frames())
}
}
impl PartialOrd for Time {
fn partial_cmp(&self, other: &Time) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl FromStr for Time {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.len() != 8 {
return Err("Time was not 8 chars long.".into());
}
if s.chars().nth(2).unwrap() != ':' || s.chars().nth(5).unwrap() != ':' {
return Err("Time was not properly formatted.".into());
}
Ok(Time {
mins: s[0..2].parse()?,
secs: s[3..5].parse()?,
frames: s[6..8].parse()?,
})
}
}
impl fmt::Display for Time {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:02}:{:02}:{:02}", self.mins, self.secs, self.frames)
}
}
impl Sub for Time {
type Output = Time;
fn sub(self, rhs: Time) -> Self::Output {
let left = self.total_frames();
let right = rhs.total_frames();
let diff = left - right;
Time::from_frames(diff)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum FileFormat {
Wave,
Mp3,
Aiff,
Binary,
Motorola,
}
impl FromStr for FileFormat {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_uppercase().as_str() {
"WAVE" => Ok(FileFormat::Wave),
"MP3" => Ok(FileFormat::Mp3),
"AIFF" => Ok(FileFormat::Aiff),
"BINARY" => Ok(FileFormat::Binary),
"MOTOROLA" => Ok(FileFormat::Motorola),
_ => Err(format!("Invalid FileFormat: {:?}", s).into()),
}
}
}
#[derive(Clone, Debug)]
pub enum TrackFlag {
Dcp,
FourChannel,
Pre,
Scms,
}
impl FromStr for TrackFlag {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_uppercase().as_str() {
"DCP" => Ok(TrackFlag::Dcp),
"4CH" => Ok(TrackFlag::FourChannel),
"PRE" => Ok(TrackFlag::Pre),
"SCMS" => Ok(TrackFlag::Scms),
s => Err(format!("invalid TrackFlag: {:?}", s).into()),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum TrackType {
Audio,
Cdg,
Mode(u8, u16),
Cdi(u16),
}
impl FromStr for TrackType {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_uppercase().as_str() {
"AUDIO" => Ok(TrackType::Audio),
"CDG" => Ok(TrackType::Cdg),
"MODE1/2048" => Ok(TrackType::Mode(1, 2048)),
"MODE1/2352" => Ok(TrackType::Mode(1, 2352)),
"MODE2/2048" => Ok(TrackType::Mode(1, 2048)),
"MODE2/2324" => Ok(TrackType::Mode(1, 2324)),
"MODE2/2336" => Ok(TrackType::Mode(1, 2336)),
"MODE2/2352" => Ok(TrackType::Mode(1, 2352)),
"CDI/2336" => Ok(TrackType::Cdi(2336)),
"CDI/2352" => Ok(TrackType::Cdi(2352)),
_ => Err(format!("Unknown track type: {:?}", s).into()),
}
}
}
pub fn parse_cue(source: &str) -> Result<Vec<Command>, Error> {
let mut tokens = tokenize(source)?;
let mut commands = Vec::new();
while tokens.len() > 0 {
commands.push(Command::consume(&mut tokens)?);
}
Ok(commands)
}