rust/src/rustdoc/config.rs

365 lines
10 KiB
Rust
Raw Normal View History

import result::result;
import std::getopts;
export output_format;
export output_style;
export config;
2012-02-29 20:10:40 -06:00
export default_config;
export parse_config;
2012-02-28 23:27:38 -06:00
export usage;
#[doc = "The type of document to output"]
enum output_format {
#[doc = "Markdown"]
markdown,
#[doc = "HTML, via markdown and pandoc"]
pandoc_html
}
#[doc = "How to organize the output"]
enum output_style {
#[doc = "All in a single document"]
doc_per_crate,
#[doc = "Each module in its own document"]
doc_per_mod
}
#[doc = "The configuration for a rustdoc session"]
type config = {
input_crate: str,
output_dir: str,
output_format: output_format,
output_style: output_style,
pandoc_cmd: option<str>
};
fn opt_output_dir() -> str { "output-dir" }
fn opt_output_format() -> str { "output-format" }
fn opt_output_style() -> str { "output-style" }
fn opt_pandoc_cmd() -> str { "pandoc-cmd" }
2012-02-28 23:27:38 -06:00
fn opt_help() -> str { "h" }
fn opts() -> ~[(getopts::opt, str)] {
~[
(getopts::optopt(opt_output_dir()),
2012-02-28 23:27:38 -06:00
"--output-dir <val> put documents here"),
(getopts::optopt(opt_output_format()),
2012-02-28 23:27:38 -06:00
"--output-format <val> either 'markdown' or 'html'"),
(getopts::optopt(opt_output_style()),
2012-02-28 23:27:38 -06:00
"--output-style <val> either 'doc-per-crate' or 'doc-per-mod'"),
(getopts::optopt(opt_pandoc_cmd()),
2012-02-28 23:27:38 -06:00
"--pandoc-cmd <val> the command for running pandoc"),
(getopts::optflag(opt_help()),
"-h print help")
]
}
2012-02-28 23:27:38 -06:00
fn usage() {
import io::println;
2012-02-28 23:27:38 -06:00
println("Usage: rustdoc ~[options] <cratefile>\n");
2012-02-28 23:27:38 -06:00
println("Options:\n");
2012-06-30 18:19:07 -05:00
for opts().each |opt| {
2012-02-28 23:27:38 -06:00
println(#fmt(" %s", tuple::second(opt)));
}
println("");
}
fn default_config(input_crate: str) -> config {
{
input_crate: input_crate,
output_dir: ".",
output_format: pandoc_html,
output_style: doc_per_mod,
pandoc_cmd: none
}
}
type program_output = fn~(str, ~[str]) -> {status: int, out: str, err: str};
fn mock_program_output(_prog: str, _args: ~[str]) -> {
status: int, out: str, err: str
} {
{
status: 0,
out: "",
err: ""
}
}
fn parse_config(args: ~[str]) -> result<config, str> {
parse_config_(args, run::program_output)
}
fn parse_config_(
args: ~[str],
program_output: program_output
) -> result<config, str> {
let args = vec::tail(args);
let opts = tuple::first(vec::unzip(opts()));
alt getopts::getopts(args, opts) {
result::ok(match) {
if vec::len(match.free) == 1u {
let input_crate = vec::head(match.free);
config_from_opts(input_crate, match, program_output)
} else if vec::is_empty(match.free) {
result::err("no crates specified")
} else {
result::err("multiple crates specified")
}
}
result::err(f) {
result::err(getopts::fail_str(f))
}
}
}
fn config_from_opts(
input_crate: str,
match: getopts::match,
program_output: program_output
) -> result<config, str> {
let config = default_config(input_crate);
let result = result::ok(config);
2012-06-30 18:19:07 -05:00
let result = do result::chain(result) |config| {
let output_dir = getopts::opt_maybe_str(match, opt_output_dir());
result::ok({
output_dir: option::get_default(output_dir, config.output_dir)
with config
})
};
2012-06-30 18:19:07 -05:00
let result = do result::chain(result) |config| {
let output_format = getopts::opt_maybe_str(
match, opt_output_format());
do option::map_default(output_format, result::ok(config))
2012-06-30 18:19:07 -05:00
|output_format| {
do result::chain(parse_output_format(output_format))
2012-06-30 18:19:07 -05:00
|output_format| {
result::ok({
output_format: output_format
with config
})
}
}
};
2012-06-30 18:19:07 -05:00
let result = do result::chain(result) |config| {
let output_style = getopts::opt_maybe_str(match, opt_output_style());
do option::map_default(output_style, result::ok(config))
2012-06-30 18:19:07 -05:00
|output_style| {
do result::chain(parse_output_style(output_style))
|output_style| {
result::ok({
output_style: output_style
with config
})
}
}
};
2012-06-30 18:19:07 -05:00
let result = do result::chain(result) |config| {
let pandoc_cmd = getopts::opt_maybe_str(match, opt_pandoc_cmd());
let pandoc_cmd = maybe_find_pandoc(
config, pandoc_cmd, program_output);
2012-06-30 18:19:07 -05:00
do result::chain(pandoc_cmd) |pandoc_cmd| {
result::ok({
pandoc_cmd: pandoc_cmd
with config
})
}
};
ret result;
}
fn parse_output_format(output_format: str) -> result<output_format, str> {
alt output_format {
"markdown" { result::ok(markdown) }
"html" { result::ok(pandoc_html) }
_ { result::err(#fmt("unknown output format '%s'", output_format)) }
}
}
fn parse_output_style(output_style: str) -> result<output_style, str> {
alt output_style {
"doc-per-crate" { result::ok(doc_per_crate) }
"doc-per-mod" { result::ok(doc_per_mod) }
_ { result::err(#fmt("unknown output style '%s'", output_style)) }
}
}
fn maybe_find_pandoc(
config: config,
maybe_pandoc_cmd: option<str>,
program_output: program_output
) -> result<option<str>, str> {
if config.output_format != pandoc_html {
ret result::ok(maybe_pandoc_cmd);
}
let possible_pandocs = alt maybe_pandoc_cmd {
some(pandoc_cmd) { ~[pandoc_cmd] }
none {
~["pandoc"] + alt os::homedir() {
2012-03-07 21:22:02 -06:00
some(dir) {
~[path::connect(dir, ".cabal/bin/pandoc")]
2012-03-07 21:22:02 -06:00
}
none { ~[] }
2012-03-07 21:22:02 -06:00
}
}
};
2012-06-30 18:19:07 -05:00
let pandoc = do vec::find(possible_pandocs) |pandoc| {
let output = program_output(pandoc, ~["--version"]);
#debug("testing pandoc cmd %s: %?", pandoc, output);
output.status == 0
};
if option::is_some(pandoc) {
result::ok(pandoc)
} else {
result::err("couldn't find pandoc")
}
}
#[test]
fn should_find_pandoc() {
let config = {
output_format: pandoc_html
with default_config("test")
};
let mock_program_output = fn~(_prog: str, _args: ~[str]) -> {
status: int, out: str, err: str
} {
{
status: 0, out: "pandoc 1.8.2.1", err: ""
}
};
let result = maybe_find_pandoc(config, none, mock_program_output);
assert result == result::ok(some("pandoc"));
}
#[test]
fn should_error_with_no_pandoc() {
let config = {
output_format: pandoc_html
with default_config("test")
};
let mock_program_output = fn~(_prog: str, _args: ~[str]) -> {
status: int, out: str, err: str
} {
{
status: 1, out: "", err: ""
}
};
let result = maybe_find_pandoc(config, none, mock_program_output);
assert result == result::err("couldn't find pandoc");
}
#[cfg(test)]
mod test {
fn parse_config(args: ~[str]) -> result<config, str> {
parse_config_(args, mock_program_output)
}
}
#[test]
fn should_error_with_no_crates() {
let config = test::parse_config(~["rustdoc"]);
assert result::get_err(config) == "no crates specified";
}
#[test]
fn should_error_with_multiple_crates() {
let config =
test::parse_config(~["rustdoc", "crate1.rc", "crate2.rc"]);
assert result::get_err(config) == "multiple crates specified";
}
#[test]
fn should_set_output_dir_to_cwd_if_not_provided() {
let config = test::parse_config(~["rustdoc", "crate.rc"]);
assert result::get(config).output_dir == ".";
}
#[test]
fn should_set_output_dir_if_provided() {
let config = test::parse_config(~[
"rustdoc", "crate.rc", "--output-dir", "snuggles"
]);
assert result::get(config).output_dir == "snuggles";
}
#[test]
fn should_set_output_format_to_pandoc_html_if_not_provided() {
let config = test::parse_config(~["rustdoc", "crate.rc"]);
assert result::get(config).output_format == pandoc_html;
}
#[test]
fn should_set_output_format_to_markdown_if_requested() {
let config = test::parse_config(~[
"rustdoc", "crate.rc", "--output-format", "markdown"
]);
assert result::get(config).output_format == markdown;
}
#[test]
fn should_set_output_format_to_pandoc_html_if_requested() {
let config = test::parse_config(~[
"rustdoc", "crate.rc", "--output-format", "html"
]);
assert result::get(config).output_format == pandoc_html;
}
#[test]
fn should_error_on_bogus_format() {
let config = test::parse_config(~[
"rustdoc", "crate.rc", "--output-format", "bogus"
]);
assert result::get_err(config) == "unknown output format 'bogus'";
}
#[test]
fn should_set_output_style_to_doc_per_mod_by_default() {
let config = test::parse_config(~["rustdoc", "crate.rc"]);
assert result::get(config).output_style == doc_per_mod;
}
#[test]
fn should_set_output_style_to_one_doc_if_requested() {
let config = test::parse_config(~[
"rustdoc", "crate.rc", "--output-style", "doc-per-crate"
]);
assert result::get(config).output_style == doc_per_crate;
}
#[test]
fn should_set_output_style_to_doc_per_mod_if_requested() {
let config = test::parse_config(~[
"rustdoc", "crate.rc", "--output-style", "doc-per-mod"
]);
assert result::get(config).output_style == doc_per_mod;
}
#[test]
fn should_error_on_bogus_output_style() {
let config = test::parse_config(~[
"rustdoc", "crate.rc", "--output-style", "bogus"
]);
assert result::get_err(config) == "unknown output style 'bogus'";
}
#[test]
fn should_set_pandoc_command_if_requested() {
let config = test::parse_config(~[
"rustdoc", "crate.rc", "--pandoc-cmd", "panda-bear-doc"
]);
assert result::get(config).pandoc_cmd == some("panda-bear-doc");
}
#[test]
fn should_set_pandoc_command_when_using_pandoc() {
let config = test::parse_config(~["rustdoc", "crate.rc"]);
assert result::get(config).pandoc_cmd == some("pandoc");
}