extern crate clap; extern crate rrecutils; extern crate rustache; use std::{fs,io}; use std::convert::From; use std::string::FromUtf8Error; use rustache::Render; struct R { rec: rrecutils::Record } impl Render for R { fn render( &self, template: &str, writer: &mut W, ) -> Result<(), rustache::RustacheError> { use rustache::HashBuilder; let mut hb = HashBuilder::new(); if let Some(ref t) = self.rec.rec_type { hb = hb.insert("%rec", t.clone()); } for field in self.rec.fields.iter() { hb = hb.insert(&field.0, field.1.clone()); } hb.render(template, writer) } } enum FormatErr { IOError(io::Error), Utf8Error(FromUtf8Error), Rustache(rustache::RustacheError), Generic(String), } impl From for FormatErr { fn from(err: io::Error) -> FormatErr { FormatErr::IOError(err) } } impl From for FormatErr { fn from(err: FromUtf8Error) -> FormatErr { FormatErr::Utf8Error(err) } } impl From for FormatErr { fn from(err: rustache::RustacheError) -> FormatErr { FormatErr::Rustache(err) } } impl From for FormatErr { fn from(err: String) -> FormatErr { FormatErr::Generic(err) } } fn run() -> Result<(), FormatErr> { let matches = clap::App::new("rr-format") .version("0.0") .author("Getty Ritter ") .about("Display the Rust AST for a Recutils file") .arg(clap::Arg::with_name("input") .short("i") .long("input") .value_name("FILE") .help("The input recfile (or - for stdin)")) .arg(clap::Arg::with_name("output") .short("o") .long("output") .value_name("FILE") .help("The desired output location (or - for stdout)")) .arg(clap::Arg::with_name("mustache") .short("m") .long("mustache") .value_name("FILE") .help("The mustache template to use")) .arg(clap::Arg::with_name("type") .short("t") .long("type") .value_name("TYPE") .takes_value(true) .help("The type of records to pass to the mustache file")) .arg(clap::Arg::with_name("joiner") .short("j") .long("joiner") .value_name("STRING") .help("The string used to separate each fragment")) .get_matches(); let stdin = io::stdin(); let input: Box = match matches.value_of("input").unwrap_or("-") { "-" => Box::new(stdin.lock()), path => Box::new(io::BufReader::new(fs::File::open(path)?)), }; let template: String = match matches.value_of("mustache") { Some(path) => { use io::Read; let mut buf = Vec::new(); fs::File::open(path)?.read_to_end(&mut buf)?; String::from_utf8(buf)? }, None => Err(format!("No template specified!"))?, }; let mut recfile = rrecutils::Recfile::parse(input)?; if let Some(typ) = matches.value_of("type") { recfile.filter_by_type(typ); } let mut output: Box = match matches.value_of("output").unwrap_or("-") { "-" => Box::new(io::stdout()), path => Box::new(fs::File::open(path)?), }; let joiner = matches.value_of("joiner"); let mut first = true; for r in recfile.records.into_iter() { if first { first = false; } else if let Some(j) = joiner { output.write(j.as_bytes())?; output.write(&['\n' as u8])?; } R { rec: r }.render(&template, &mut output.as_mut())?; } Ok(()) } fn main() { use FormatErr::*; match run() { Ok(()) => (), Err(IOError(_)) => panic!("IO Error"), Err(Utf8Error(_)) => panic!("Cannot decode as UTF-8"), Err(Rustache(r)) => panic!("Rustache error: {:?}", r), Err(Generic(s)) => panic!("{}", s), } }