extern crate getopts;
extern crate markdown_toc;
use markdown_toc::*;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
use std::str::FromStr;
fn parse_command(opts: &mut getopts::Options, args: &[String]) -> Result<Config, ()> {
opts.optflag("h", "help", "print this help message")
.optopt(
"",
"bullet",
"Custom bullet of the ToC list. (default: \"1.\")",
"{str}",
)
.optopt(
"",
"indent",
"Indentation of the ToC list. (default: 4)",
"{int}",
)
.optopt("", "max-depth", "Max depth of headers to include.", "{int}")
.optopt(
"",
"min-depth",
"Min depth of headers to include. (default: 0)",
"{int}",
)
.optopt(
"",
"header",
"Custom header of the ToC. (default: \"## Table of Contents\")",
"{str}",
)
.optflag("", "no-link", "Exclude links in ToC")
.optflag("", "no-header", "Exclude the header of ToC");
let opt_matches = opts.parse(args).map_err(|_| ())?;
if opt_matches.opt_present("h") {
return Err(());
}
let default = Config::default();
macro_rules! get_opt_or_default {
($o:expr, $p:ident) => {
if let Some(val) = opt_matches.opt_get($o).map_err(|_| ())? {
val
} else {
default.$p
}
};
}
macro_rules! get_opt_or_default_option {
($o:expr, $p:ident) => {
opt_matches.opt_get($o).map_err(|_| ())?.or(default.$p)
};
}
Ok(Config {
input_file: if !opt_matches.free.is_empty() {
if opt_matches.free[0] == "-" {
InputFile::StdIn
} else {
InputFile::Path(PathBuf::from(&opt_matches.free[0]))
}
} else {
return Err(());
},
bullet: get_opt_or_default!("bullet", bullet),
indent: get_opt_or_default!("indent", indent),
max_depth: get_opt_or_default_option!("max-depth", max_depth),
min_depth: get_opt_or_default!("min-depth", min_depth),
no_link: opt_matches.opt_present("no-link"),
header: if opt_matches.opt_present("no-header") {
None
} else {
get_opt_or_default_option!("header", header)
},
})
}
fn print_help(program: &str, opts: &getopts::Options) {
let brief = format!("Usage: {} FILE [options]\n\n FILE The Markdown file to parse for table of contents,\n or \"-\" to read from stdin", program);
eprint!("{}", opts.usage(&brief));
}
fn main() {
let args = std::env::args().collect::<Vec<_>>();
let program = args[0].clone();
let mut opts = getopts::Options::new();
let config = match parse_command(&mut opts, &args[1..]) {
Ok(c) => c,
Err(_) => {
print_help(&program, &opts);
return;
}
};
let mut content = String::new();
match config.input_file {
InputFile::StdIn => std::io::stdin().read_to_string(&mut content),
InputFile::Path(ref p) => File::open(p).unwrap().read_to_string(&mut content),
}.unwrap();
println!("");
if let Some(ref header) = config.header {
println!("{}\n", header);
}
content
.lines()
.map(Heading::from_str)
.filter_map(Result::ok)
.filter_map(|h| h.format(&config))
.for_each(|l| {
println!("{}", l);
});
}