extern crate ini as _ini;
use std::io::{self, Write, Read};
use std::error::Error as ErrorT;
use std::num::ParseIntError;
use _ini::ini;
use std::fmt;
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct PlaylistElement {
pub path: String,
pub title: Option<String>,
pub len: ElementLength,
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum ElementLength {
Seconds(u64),
Unknown,
}
#[derive(Debug)]
pub enum ParseError {
InvalidVersion(u64),
MissingPlaylistSection,
MissingKey(String),
InvalidInteger(ParseIntError),
Ini(ini::Error),
}
pub fn parse<R: Read>(what: &mut R) -> Result<Vec<PlaylistElement>, ParseError> {
let p = ini::Ini::read_from(what)?;
let play = p.section(Some("playlist")).ok_or(ParseError::MissingPlaylistSection)?;
if let Some(v) = play.get("Version") {
let v = v.parse()?;
if v != 2 {
return Err(ParseError::InvalidVersion(v));
}
}
if let Some(e) = play.get("NumberOfEntries").or_else(|| play.get("numberofentries")).or_else(|| play.get("NumberOfEvents")) {
let e = e.parse()?;
let mut elems = Vec::with_capacity(e);
for i in 1..e + 1 {
elems.push(PlaylistElement {
path: play.get(&format!("File{}", i)).ok_or_else(|| ParseError::MissingKey(format!("File{}", i)))?.clone(),
title: play.get(&format!("Title{}", i)).cloned(),
len: ElementLength::parse(play.get(&format!("Length{}", i)))?,
})
}
Ok(elems)
} else {
Err(ParseError::MissingKey("NumberOfEntries|numberofentries|NumberOfEvents".to_string()))
}
}
pub fn write<'i, I: IntoIterator<Item = &'i PlaylistElement>, W: Write>(what: I, to: &mut W) -> io::Result<()> {
writeln!(to, "[playlist]")?;
let mut ent = 0u64;
for (i, &PlaylistElement { ref path, ref title, ref len }) in what.into_iter().enumerate() {
writeln!(to, "File{}={}", i + 1, path)?;
if let Some(title) = title.as_ref() {
writeln!(to, "Title{}={}", i + 1, title)?;
}
if let ElementLength::Seconds(s) = *len {
writeln!(to, "Length{}={}", i + 1, s)?;
}
writeln!(to, "")?;
ent += 1;
}
writeln!(to, "NumberOfEntries={}", ent)?;
writeln!(to, "Version=2")?;
Ok(())
}
impl ElementLength {
fn parse<S: AsRef<str>>(what: Option<S>) -> Result<ElementLength, ParseError> {
if let Some(what) = what {
let what = what.as_ref();
if what == "-1" {
Ok(ElementLength::Unknown)
} else {
Ok(ElementLength::Seconds(what.parse()?))
}
} else {
Ok(ElementLength::Unknown)
}
}
}
impl From<ini::Error> for ParseError {
fn from(e: ini::Error) -> ParseError {
ParseError::Ini(e)
}
}
impl From<ParseIntError> for ParseError {
fn from(e: ParseIntError) -> ParseError {
ParseError::InvalidInteger(e)
}
}
impl ErrorT for ParseError {
#[allow(deprecated)]
fn description(&self) -> &str {
match *self {
ParseError::InvalidVersion(_) => "invalid version specified",
ParseError::MissingPlaylistSection => "[playlist] section missing",
ParseError::MissingKey(_) => "required key missing",
ParseError::InvalidInteger(ref e) => e.description(),
ParseError::Ini(ref e) => e.description(),
}
}
fn source(&self) -> Option<&(dyn ErrorT + 'static)> {
match *self {
ParseError::InvalidInteger(ref e) => Some(e),
ParseError::Ini(ref e) => Some(e),
_ => None,
}
}
}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ParseError::InvalidVersion(v) => write!(f, "Invalid version {} specified", v),
ParseError::MissingPlaylistSection => write!(f, "Missing [playlist] section"),
ParseError::MissingKey(ref k) => write!(f, "Key \"{}\" missing", k),
ParseError::InvalidInteger(ref e) => e.fmt(f),
ParseError::Ini(ref e) => e.fmt(f),
}
}
}
impl Clone for ParseError {
fn clone(&self) -> ParseError {
match *self {
ParseError::InvalidVersion(v) => ParseError::InvalidVersion(v),
ParseError::MissingPlaylistSection => ParseError::MissingPlaylistSection,
ParseError::MissingKey(ref k) => ParseError::MissingKey(k.clone()),
ParseError::InvalidInteger(ref e) => ParseError::InvalidInteger(e.clone()),
ParseError::Ini(ref e) => ParseError::Ini(ini::Error { msg: e.msg.clone(), ..*e }),
}
}
}
impl PartialEq for ParseError {
fn eq(&self, rhs: &ParseError) -> bool {
match (self, rhs) {
(&ParseError::InvalidVersion(v), &ParseError::InvalidVersion(rv)) => v == rv,
(&ParseError::MissingPlaylistSection, &ParseError::MissingPlaylistSection) => true,
(&ParseError::MissingKey(ref k), &ParseError::MissingKey(ref rk)) => k == rk,
(&ParseError::InvalidInteger(ref e), &ParseError::InvalidInteger(ref re)) => e == re,
(&ParseError::Ini(ref e), &ParseError::Ini(ref re)) => e.line == re.line && e.col == re.col && e.msg == re.msg,
(_, _) => false,
}
}
}