use chrono::{Local, TimeZone}; use std::ffi::OsString; use std::fs; use std::path::Path; use std::path::PathBuf; use std::time::UNIX_EPOCH; use structopt::StructOpt; use anyhow::Result; use subplot::{get_basedir_from, resource, Document, Style}; // Define the command line arguments. #[derive(Debug, StructOpt)] #[structopt(name = "docgen", about = "Subplot document generator.")] struct Opt { // One or more input filename. #[structopt(parse(from_os_str))] filenames: Vec, // Set output file name. #[structopt(name = "FILE", long = "--output", short = "-o", parse(from_os_str))] output: PathBuf, // Set date. #[structopt(name = "DATE", long = "--date")] date: Option, #[structopt(flatten)] resources: resource::ResourceOpts, } fn main() -> Result<()> { let opt = Opt::from_args(); opt.resources.handle(); let mut pandoc = pandoc::new(); let first_file = &opt.filenames[0]; let mut style = Style::default(); style.links_as_notes = opt.output.extension() == Some(&OsString::from("pdf")); let basedir = get_basedir_from(first_file)?; let mut doc = Document::from_file(&basedir, &first_file, style)?; doc.lint()?; if !doc.check_named_files_exist()? { eprintln!("Continuing despite warnings"); } // Metadata date from command line or file mtime. However, we // can't set it directly, since we don't want to override the date // in the actual document, if given, so we only set // user-provided-date. Our parsing code will use that if date is // not document metadata. let date = if let Some(date) = opt.date { date } else if let Some(date) = doc.meta().date() { date.to_string() } else { mtime_formatted(mtime(first_file)?) }; pandoc.add_option(pandoc::PandocOption::Meta("date".to_string(), Some(date))); pandoc.add_option(pandoc::PandocOption::TableOfContents); pandoc.add_option(pandoc::PandocOption::Standalone); pandoc.add_option(pandoc::PandocOption::NumberSections); if need_output(&mut doc, &opt.output) { doc.typeset(); pandoc.set_input_format(pandoc::InputFormat::Json, vec![]); pandoc.set_input(pandoc::InputKind::Pipe(doc.ast()?)); pandoc.set_output(pandoc::OutputKind::File(opt.output.clone())); pandoc.execute()?; } Ok(()) } fn mtime(filename: &Path) -> Result<(u64, u32)> { let mtime = fs::metadata(filename)?.modified()?; let mtime = mtime.duration_since(UNIX_EPOCH)?; Ok((mtime.as_secs(), mtime.subsec_nanos())) } fn mtime_formatted(mtime: (u64, u32)) -> String { let secs: i64 = format!("{}", mtime.0).parse().unwrap_or(0); let dt = Local.timestamp(secs, mtime.1); dt.format("%Y-%m-%d %H:%M").to_string() } fn need_output(doc: &mut subplot::Document, output: &Path) -> bool { let output = match mtime(output) { Err(_) => return true, Ok(ts) => ts, }; for filename in doc.sources() { let source = match mtime(&filename) { Err(_) => return true, Ok(ts) => ts, }; if source >= output { return true; } } false }