use super::{FileFormat, Time, Token, TrackFlag, TrackType};
use errors::Error;
use std::str::FromStr;
#[derive(Clone, Debug)]
pub enum Command {
Catalog(String),
Cdtextfile(String),
File(String, FileFormat),
Flags(Vec<TrackFlag>),
Index(u32, Time),
Isrc(String),
Performer(String),
Postgap(Time),
Pregap(Time),
Rem(String, Token),
Songwriter(String),
Title(String),
Track(u32, TrackType),
}
fn consume_token(tokens: &mut Vec<Token>) -> Result<Token, Error> {
if tokens.len() == 0 {
Err("No tokens left!".into())
} else {
Ok(tokens.remove(0))
}
}
fn consume_time(tokens: &mut Vec<Token>) -> Result<Time, Error> {
match consume_token(tokens)? {
Token::Time(duration) => Ok(duration),
t => Err(format!("Expected duration but found {:?} instead", t).into()),
}
}
fn consume_number(tokens: &mut Vec<Token>) -> Result<u32, Error> {
match consume_token(tokens)? {
Token::Number(num) => Ok(num),
t => Err(format!("Expeceted number but found {:?} instead", t).into()),
}
}
fn consume_string(tokens: &mut Vec<Token>) -> Result<String, Error> {
match consume_token(tokens)? {
Token::String(s) => Ok(s),
t => Err(format!("Expeceted string but found {:?} instead", t).into()),
}
}
impl Command {
pub(crate) fn consume(tokens: &mut Vec<Token>) -> Result<Command, Error> {
let keyword = consume_string(tokens)?;
match keyword.to_uppercase().as_str() {
"CATALOG" => Ok(Command::Catalog(format!("{:013}", consume_number(tokens)?))),
"CDTEXTFILE" => Ok(Command::Cdtextfile(consume_string(tokens)?)),
"FILE" => Ok(Command::File(
consume_string(tokens)?,
consume_string(tokens)?.parse()?,
)),
"FLAGS" => {
let mut flags = Vec::<TrackFlag>::new();
while tokens.len() > 0 {
let token = tokens.remove(0);
let ok = match token {
Token::String(ref s) => match TrackFlag::from_str(s.as_str()) {
Ok(flag) => {
flags.push(flag);
true
}
Err(_) => false,
},
_ => false,
};
if !ok {
tokens.insert(0, token);
break;
}
}
if flags.len() == 0 {
Err("Encountered FLAGS command without succeeding TrackFlag".into())
} else {
Ok(Command::Flags(flags))
}
}
"INDEX" => Ok(Command::Index(
consume_number(tokens)?,
consume_time(tokens)?,
)),
"ISRC" => Ok(Command::Isrc(consume_string(tokens)?)),
"PERFORMER" => Ok(Command::Performer(consume_string(tokens)?)),
"POSTGAP" => Ok(Command::Postgap(consume_time(tokens)?)),
"PREGAP" => Ok(Command::Pregap(consume_time(tokens)?)),
"REM" => Ok(Command::Rem(
consume_string(tokens)?,
consume_token(tokens)?,
)),
"SONGWRITER" => Ok(Command::Songwriter(consume_string(tokens)?)),
"TITLE" => Ok(Command::Title(consume_string(tokens)?)),
"TRACK" => Ok(Command::Track(
consume_number(tokens)?,
consume_string(tokens)?.parse()?,
)),
cmd => Err(format!("Invalid command: {:?}", cmd).into()),
}
}
}