rust/src/librustdoc/config.rs

385 lines
11 KiB
Rust
Raw Normal View History

2012-09-05 10:41:47 -07:00
use result::Result;
use std::getopts;
2012-09-18 16:48:40 -07:00
export OutputFormat;
export OutputStyle;
export Config;
2012-02-29 18:10:40 -08:00
export default_config;
export parse_config;
2012-02-28 21:27:38 -08:00
export usage;
2012-09-18 16:48:40 -07:00
export Markdown, PandocHtml;
export DocPerCrate, DocPerMod;
/// The type of document to output
2012-09-18 16:48:40 -07:00
enum OutputFormat {
/// Markdown
2012-09-18 16:48:40 -07:00
Markdown,
/// HTML, via markdown and pandoc
2012-09-18 16:48:40 -07:00
PandocHtml
}
impl OutputFormat : cmp::Eq {
pure fn eq(other: &OutputFormat) -> bool {
(self as uint) == ((*other) as uint)
}
pure fn ne(other: &OutputFormat) -> bool { !self.eq(other) }
}
2012-08-27 16:26:35 -07:00
/// How to organize the output
2012-09-18 16:48:40 -07:00
enum OutputStyle {
/// All in a single document
2012-09-18 16:48:40 -07:00
DocPerCrate,
/// Each module in its own document
2012-09-18 16:48:40 -07:00
DocPerMod
}
impl OutputStyle : cmp::Eq {
pure fn eq(other: &OutputStyle) -> bool {
(self as uint) == ((*other) as uint)
}
pure fn ne(other: &OutputStyle) -> bool { !self.eq(other) }
}
2012-08-27 16:26:35 -07:00
/// The configuration for a rustdoc session
2012-09-18 16:48:40 -07:00
type Config = {
input_crate: Path,
output_dir: Path,
2012-09-18 16:48:40 -07:00
output_format: OutputFormat,
output_style: OutputStyle,
2012-08-20 12:23:37 -07:00
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" }
fn opt_help() -> ~str { ~"h" }
2012-08-11 10:08:42 -04:00
fn opts() -> ~[(getopts::Opt, ~str)] {
~[
(getopts::optopt(opt_output_dir()),
~"--output-dir <val> put documents here"),
(getopts::optopt(opt_output_format()),
~"--output-format <val> either 'markdown' or 'html'"),
(getopts::optopt(opt_output_style()),
~"--output-style <val> either 'doc-per-crate' or 'doc-per-mod'"),
(getopts::optopt(opt_pandoc_cmd()),
~"--pandoc-cmd <val> the command for running pandoc"),
2012-02-28 21:27:38 -08:00
(getopts::optflag(opt_help()),
~"-h print help")
]
}
2012-02-28 21:27:38 -08:00
fn usage() {
use io::println;
2012-02-28 21:27:38 -08:00
2012-09-11 19:40:20 -07:00
println(~"Usage: rustdoc [options] <cratefile>\n");
println(~"Options:\n");
2012-06-30 16:19:07 -07:00
for opts().each |opt| {
2012-08-22 17:24:52 -07:00
println(fmt!(" %s", opt.second()));
2012-02-28 21:27:38 -08:00
}
println(~"");
2012-02-28 21:27:38 -08:00
}
2012-09-18 16:48:40 -07:00
fn default_config(input_crate: &Path) -> Config {
{
input_crate: *input_crate,
output_dir: Path("."),
2012-09-18 16:48:40 -07:00
output_format: PandocHtml,
output_style: DocPerMod,
2012-08-20 12:23:37 -07:00
pandoc_cmd: None
}
}
2012-09-18 16:48:40 -07:00
type ProgramOutput = fn~((&str), (&[~str])) ->
{status: int, out: ~str, err: ~str};
2012-08-14 14:34:35 -07:00
fn mock_program_output(_prog: &str, _args: &[~str]) -> {
status: int, out: ~str, err: ~str
} {
{
status: 0,
out: ~"",
err: ~""
}
}
2012-09-18 16:48:40 -07:00
fn parse_config(args: ~[~str]) -> Result<Config, ~str> {
parse_config_(args, run::program_output)
}
fn parse_config_(
args: ~[~str],
2012-09-18 16:48:40 -07:00
program_output: ProgramOutput
) -> Result<Config, ~str> {
let args = vec::tail(args);
let opts = vec::unzip(opts()).first();
2012-08-06 12:34:08 -07:00
match getopts::getopts(args, opts) {
2012-08-26 16:54:31 -07:00
result::Ok(matches) => {
if vec::len(matches.free) == 1u {
let input_crate = Path(vec::head(matches.free));
config_from_opts(&input_crate, matches, program_output)
} else if vec::is_empty(matches.free) {
2012-08-26 16:54:31 -07:00
result::Err(~"no crates specified")
} else {
2012-08-26 16:54:31 -07:00
result::Err(~"multiple crates specified")
}
}
2012-08-26 16:54:31 -07:00
result::Err(f) => {
result::Err(getopts::fail_str(f))
}
}
}
fn config_from_opts(
input_crate: &Path,
2012-08-11 10:08:42 -04:00
matches: getopts::Matches,
2012-09-18 16:48:40 -07:00
program_output: ProgramOutput
) -> Result<Config, ~str> {
let config = default_config(input_crate);
2012-08-26 16:54:31 -07:00
let result = result::Ok(config);
2012-06-30 16:19:07 -07:00
let result = do result::chain(result) |config| {
let output_dir = getopts::opt_maybe_str(matches, opt_output_dir());
let output_dir = output_dir.map(|s| Path(*s));
2012-08-26 16:54:31 -07:00
result::Ok({
2012-09-21 19:37:57 -07:00
output_dir: output_dir.get_default(config.output_dir),
2012-09-04 13:29:32 -07:00
.. config
})
};
2012-06-30 16:19:07 -07:00
let result = do result::chain(result) |config| {
let output_format = getopts::opt_maybe_str(
matches, opt_output_format());
2012-09-21 19:37:57 -07:00
do output_format.map_default(result::Ok(config))
2012-06-30 16:19:07 -07:00
|output_format| {
do result::chain(parse_output_format(*output_format))
2012-06-30 16:19:07 -07:00
|output_format| {
2012-08-26 16:54:31 -07:00
result::Ok({
2012-09-04 13:29:32 -07:00
output_format: output_format,
.. config
})
}
}
};
2012-06-30 16:19:07 -07:00
let result = do result::chain(result) |config| {
let output_style =
getopts::opt_maybe_str(matches, opt_output_style());
2012-09-21 19:37:57 -07:00
do output_style.map_default(result::Ok(config))
2012-06-30 16:19:07 -07:00
|output_style| {
do result::chain(parse_output_style(*output_style))
2012-06-30 16:19:07 -07:00
|output_style| {
2012-08-26 16:54:31 -07:00
result::Ok({
2012-09-04 13:29:32 -07:00
output_style: output_style,
.. config
})
}
}
};
2012-06-30 16:19:07 -07:00
let result = do result::chain(result) |config| {
let pandoc_cmd = getopts::opt_maybe_str(matches, opt_pandoc_cmd());
let pandoc_cmd = maybe_find_pandoc(
config, pandoc_cmd, program_output);
2012-06-30 16:19:07 -07:00
do result::chain(pandoc_cmd) |pandoc_cmd| {
2012-08-26 16:54:31 -07:00
result::Ok({
2012-09-04 13:29:32 -07:00
pandoc_cmd: pandoc_cmd,
.. config
})
}
};
2012-08-01 17:30:05 -07:00
return result;
}
2012-09-18 16:48:40 -07:00
fn parse_output_format(output_format: ~str) -> Result<OutputFormat, ~str> {
2012-08-06 12:34:08 -07:00
match output_format {
2012-09-18 16:48:40 -07:00
~"markdown" => result::Ok(Markdown),
~"html" => result::Ok(PandocHtml),
2012-08-26 16:54:31 -07:00
_ => result::Err(fmt!("unknown output format '%s'", output_format))
}
}
2012-09-18 16:48:40 -07:00
fn parse_output_style(output_style: ~str) -> Result<OutputStyle, ~str> {
2012-08-06 12:34:08 -07:00
match output_style {
2012-09-18 16:48:40 -07:00
~"doc-per-crate" => result::Ok(DocPerCrate),
~"doc-per-mod" => result::Ok(DocPerMod),
2012-08-26 16:54:31 -07:00
_ => result::Err(fmt!("unknown output style '%s'", output_style))
}
}
fn maybe_find_pandoc(
2012-09-18 16:48:40 -07:00
config: Config,
2012-08-20 12:23:37 -07:00
maybe_pandoc_cmd: Option<~str>,
2012-09-18 16:48:40 -07:00
program_output: ProgramOutput
2012-08-26 16:54:31 -07:00
) -> Result<Option<~str>, ~str> {
2012-09-18 16:48:40 -07:00
if config.output_format != PandocHtml {
2012-08-26 16:54:31 -07:00
return result::Ok(maybe_pandoc_cmd);
}
2012-08-06 12:34:08 -07:00
let possible_pandocs = match maybe_pandoc_cmd {
2012-08-20 12:23:37 -07:00
Some(pandoc_cmd) => ~[pandoc_cmd],
None => {
2012-08-06 12:34:08 -07:00
~[~"pandoc"] + match os::homedir() {
2012-08-20 12:23:37 -07:00
Some(dir) => {
~[dir.push_rel(&Path(".cabal/bin/pandoc")).to_str()]
2012-03-07 19:22:02 -08:00
}
2012-08-20 12:23:37 -07:00
None => ~[]
2012-03-07 19:22:02 -08:00
}
}
};
2012-06-30 16:19:07 -07:00
let pandoc = do vec::find(possible_pandocs) |pandoc| {
2012-09-27 22:20:47 -07:00
let output = program_output(*pandoc, ~[~"--version"]);
debug!("testing pandoc cmd %s: %?", *pandoc, output);
output.status == 0
};
2012-09-21 19:37:57 -07:00
if pandoc.is_some() {
2012-08-26 16:54:31 -07:00
result::Ok(pandoc)
} else {
2012-08-26 16:54:31 -07:00
result::Err(~"couldn't find pandoc")
}
}
#[test]
fn should_find_pandoc() {
let config = {
2012-09-18 16:48:40 -07:00
output_format: PandocHtml,
2012-09-04 13:29:32 -07:00
.. default_config(&Path("test"))
};
2012-08-14 14:34:35 -07:00
let mock_program_output = fn~(_prog: &str, _args: &[~str]) -> {
status: int, out: ~str, err: ~str
} {
{
status: 0, out: ~"pandoc 1.8.2.1", err: ~""
}
};
2012-08-20 12:23:37 -07:00
let result = maybe_find_pandoc(config, None, mock_program_output);
2012-08-26 16:54:31 -07:00
assert result == result::Ok(Some(~"pandoc"));
}
#[test]
fn should_error_with_no_pandoc() {
let config = {
2012-09-18 16:48:40 -07:00
output_format: PandocHtml,
2012-09-04 13:29:32 -07:00
.. default_config(&Path("test"))
};
2012-08-14 14:34:35 -07:00
let mock_program_output = fn~(_prog: &str, _args: &[~str]) -> {
status: int, out: ~str, err: ~str
} {
{
status: 1, out: ~"", err: ~""
}
};
2012-08-20 12:23:37 -07:00
let result = maybe_find_pandoc(config, None, mock_program_output);
2012-08-26 16:54:31 -07:00
assert result == result::Err(~"couldn't find pandoc");
}
#[cfg(test)]
mod test {
#[legacy_exports];
2012-09-18 16:48:40 -07:00
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"]);
2012-09-25 16:23:04 -07:00
assert config.get_err() == ~"no crates specified";
}
#[test]
fn should_error_with_multiple_crates() {
let config =
test::parse_config(~[~"rustdoc", ~"crate1.rc", ~"crate2.rc"]);
2012-09-25 16:23:04 -07:00
assert config.get_err() == ~"multiple crates specified";
}
#[test]
fn should_set_output_dir_to_cwd_if_not_provided() {
let config = test::parse_config(~[~"rustdoc", ~"crate.rc"]);
2012-09-25 16:23:04 -07:00
assert config.get().output_dir == Path(".");
}
#[test]
fn should_set_output_dir_if_provided() {
let config = test::parse_config(~[
~"rustdoc", ~"crate.rc", ~"--output-dir", ~"snuggles"
]);
2012-09-25 16:23:04 -07:00
assert config.get().output_dir == Path("snuggles");
}
#[test]
fn should_set_output_format_to_pandoc_html_if_not_provided() {
let config = test::parse_config(~[~"rustdoc", ~"crate.rc"]);
2012-09-25 16:23:04 -07:00
assert config.get().output_format == PandocHtml;
}
#[test]
fn should_set_output_format_to_markdown_if_requested() {
let config = test::parse_config(~[
~"rustdoc", ~"crate.rc", ~"--output-format", ~"markdown"
]);
2012-09-25 16:23:04 -07:00
assert config.get().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"
]);
2012-09-25 16:23:04 -07:00
assert config.get().output_format == PandocHtml;
}
#[test]
fn should_error_on_bogus_format() {
let config = test::parse_config(~[
~"rustdoc", ~"crate.rc", ~"--output-format", ~"bogus"
]);
2012-09-25 16:23:04 -07:00
assert config.get_err() == ~"unknown output format 'bogus'";
}
#[test]
fn should_set_output_style_to_doc_per_mod_by_default() {
let config = test::parse_config(~[~"rustdoc", ~"crate.rc"]);
2012-09-25 16:23:04 -07:00
assert config.get().output_style == DocPerMod;
}
#[test]
fn should_set_output_style_to_one_doc_if_requested() {
let config = test::parse_config(~[
~"rustdoc", ~"crate.rc", ~"--output-style", ~"doc-per-crate"
]);
2012-09-25 16:23:04 -07:00
assert config.get().output_style == DocPerCrate;
}
#[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"
]);
2012-09-25 16:23:04 -07:00
assert config.get().output_style == DocPerMod;
}
#[test]
fn should_error_on_bogus_output_style() {
let config = test::parse_config(~[
~"rustdoc", ~"crate.rc", ~"--output-style", ~"bogus"
]);
2012-09-25 16:23:04 -07:00
assert config.get_err() == ~"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"
]);
2012-09-25 16:23:04 -07:00
assert config.get().pandoc_cmd == Some(~"panda-bear-doc");
}
#[test]
fn should_set_pandoc_command_when_using_pandoc() {
let config = test::parse_config(~[~"rustdoc", ~"crate.rc"]);
2012-09-25 16:23:04 -07:00
assert config.get().pandoc_cmd == Some(~"pandoc");
}