use cast;
use image::Rgb;
use regex::Regex;
use std::fs;
use std::io;
use std::io::prelude::*;
use std::path::Path;
use errors::*;
use sub;
named!(hex_digit<u8>,
map!(one_of!(b"0123456789abcdefABCDEF"), |c: char| -> u8 {
cast::u8(c.to_digit(16).unwrap()).unwrap()
})
);
named!(hex_u8<u8>,
do_parse!(
h1: call!(hex_digit) >>
h2: call!(hex_digit) >>
(h1 << 4 | h2)
)
);
named!(rgb<Rgb<u8>>,
map!(count_fixed!(u8, call!(hex_u8), 3), |rgb| { Rgb { data: rgb } })
);
#[test]
fn parse_rgb() {
use nom::IResult;
assert_eq!(rgb(&b"1234ab"[..]),
IResult::Done(&b""[..], Rgb::<u8> { data: [0x12, 0x34, 0xab] }));
}
pub type Palette = [Rgb<u8>; 16];
named!(palette<Palette>,
map_res!(separated_list!(tag!(b", "), call!(rgb)), |vec: Vec<Rgb<u8>>| {
if vec.len() != 16 {
let err: Error = "Palettes must have 16 entries".into();
return Err(err);
}
let mut result = [Rgb { data: [0, 0, 0] }; 16];
<[Rgb<u8>; 16] as AsMut<_>>::as_mut(&mut result)
.clone_from_slice(&vec[0..16]);
Ok(result)
})
);
#[test]
fn parse_palette() {
use nom::IResult;
let input = b"\
000000, f0f0f0, cccccc, 999999, 3333fa, 1111bb, fa3333, bb1111, \
33fa33, 11bb11, fafa33, bbbb11, fa33fa, bb11bb, 33fafa, 11bbbb";
assert_eq!(palette(input),
IResult::Done(&[][..],
[Rgb { data: [0x00, 0x00, 0x00] },
Rgb { data: [0xf0, 0xf0, 0xf0] },
Rgb { data: [0xcc, 0xcc, 0xcc] },
Rgb { data: [0x99, 0x99, 0x99] },
Rgb { data: [0x33, 0x33, 0xfa] },
Rgb { data: [0x11, 0x11, 0xbb] },
Rgb { data: [0xfa, 0x33, 0x33] },
Rgb { data: [0xbb, 0x11, 0x11] },
Rgb { data: [0x33, 0xfa, 0x33] },
Rgb { data: [0x11, 0xbb, 0x11] },
Rgb { data: [0xfa, 0xfa, 0x33] },
Rgb { data: [0xbb, 0xbb, 0x11] },
Rgb { data: [0xfa, 0x33, 0xfa] },
Rgb { data: [0xbb, 0x11, 0xbb] },
Rgb { data: [0x33, 0xfa, 0xfa] },
Rgb { data: [0x11, 0xbb, 0xbb] }]));
}
#[derive(Debug)]
pub struct Index {
palette: Palette,
sub_data: Vec<u8>,
}
impl Index {
pub fn open<P: AsRef<Path>>(path: P) -> Result<Index> {
lazy_static! {
static ref KEY_VALUE: Regex =
Regex::new("^([A-Za-z/ ]+): (.*)").unwrap();
}
let path = path.as_ref();
let mut sub_path = path.to_owned();
sub_path.set_extension("sub");
let mkerr = || -> Error {
format!("Could not parse {}", path.display()).into()
};
let mut palette_val: Option<Palette> = None;
let f = fs::File::open(path).chain_err(&mkerr)?;
let input = io::BufReader::new(f);
for line in input.lines() {
let line = line.chain_err(&mkerr)?;
if let Some(cap) = KEY_VALUE.captures(&line) {
let key = cap.get(1).unwrap().as_str();
let val = cap.get(2).unwrap().as_str();
match key {
"palette" => {
palette_val =
Some(palette(val.as_bytes()).to_vobsub_result()?);
}
_ => trace!("Unimplemented idx key: {}", key),
}
}
}
let mut sub = fs::File::open(sub_path)?;
let mut sub_data = vec![];
sub.read_to_end(&mut sub_data)?;
Ok(Index {
palette: palette_val
.ok_or_else(|| Error::from(ErrorKind::MissingKey("palette")))
.chain_err(&mkerr)?,
sub_data: sub_data,
})
}
pub fn palette(&self) -> &Palette {
&self.palette
}
pub fn subtitles(&self) -> sub::Subtitles {
sub::subtitles(&self.sub_data)
}
}
#[test]
fn parse_index() {
use env_logger;
let _ = env_logger::init();
let idx = Index::open("../fixtures/example.idx").unwrap();
assert_eq!(idx.palette()[0], Rgb { data: [0x00, 0x00, 0x00] });
assert_eq!(idx.palette()[15], Rgb { data: [0x11, 0xbb, 0xbb] });
}