304 lines
7.8 KiB
Rust
304 lines
7.8 KiB
Rust
import doc::item_utils;
|
|
|
|
export writeinstr;
|
|
export writer;
|
|
export writer_factory;
|
|
export writer_util;
|
|
export writer_utils;
|
|
export make_writer_factory;
|
|
export future_writer_factory;
|
|
export make_filename;
|
|
|
|
enum writeinstr {
|
|
write(~str),
|
|
done
|
|
}
|
|
|
|
type writer = fn~(+writeinstr);
|
|
type writer_factory = fn~(page: doc::page) -> writer;
|
|
|
|
trait writer_utils {
|
|
fn write_str(str: ~str);
|
|
fn write_line(str: ~str);
|
|
fn write_done();
|
|
}
|
|
|
|
impl writer: writer_utils {
|
|
fn write_str(str: ~str) {
|
|
self(write(str));
|
|
}
|
|
|
|
fn write_line(str: ~str) {
|
|
self.write_str(str + ~"\n");
|
|
}
|
|
|
|
fn write_done() {
|
|
self(done)
|
|
}
|
|
}
|
|
|
|
fn make_writer_factory(config: config::config) -> writer_factory {
|
|
match config.output_format {
|
|
config::markdown => {
|
|
markdown_writer_factory(config)
|
|
}
|
|
config::pandoc_html => {
|
|
pandoc_writer_factory(config)
|
|
}
|
|
}
|
|
}
|
|
|
|
fn markdown_writer_factory(config: config::config) -> writer_factory {
|
|
fn~(page: doc::page) -> writer {
|
|
markdown_writer(config, page)
|
|
}
|
|
}
|
|
|
|
fn pandoc_writer_factory(config: config::config) -> writer_factory {
|
|
fn~(page: doc::page) -> writer {
|
|
pandoc_writer(config, page)
|
|
}
|
|
}
|
|
|
|
fn markdown_writer(
|
|
config: config::config,
|
|
page: doc::page
|
|
) -> writer {
|
|
let filename = make_local_filename(config, page);
|
|
do generic_writer |markdown| {
|
|
write_file(filename, markdown);
|
|
}
|
|
}
|
|
|
|
fn pandoc_writer(
|
|
config: config::config,
|
|
page: doc::page
|
|
) -> writer {
|
|
assert option::is_some(config.pandoc_cmd);
|
|
let pandoc_cmd = option::get(config.pandoc_cmd);
|
|
let filename = make_local_filename(config, page);
|
|
|
|
let pandoc_args = ~[
|
|
~"--standalone",
|
|
~"--section-divs",
|
|
~"--from=markdown",
|
|
~"--to=html",
|
|
~"--css=rust.css",
|
|
~"--output=" + filename
|
|
];
|
|
|
|
do generic_writer |markdown| {
|
|
import io::writer_util;
|
|
|
|
debug!{"pandoc cmd: %s", pandoc_cmd};
|
|
debug!{"pandoc args: %s", str::connect(pandoc_args, ~" ")};
|
|
|
|
let pipe_in = os::pipe();
|
|
let pipe_out = os::pipe();
|
|
let pipe_err = os::pipe();
|
|
let pid = run::spawn_process(
|
|
pandoc_cmd, pandoc_args, &none, &none,
|
|
pipe_in.in, pipe_out.out, pipe_err.out);
|
|
|
|
let writer = io::fd_writer(pipe_in.out, false);
|
|
writer.write_str(markdown);
|
|
|
|
os::close(pipe_in.in);
|
|
os::close(pipe_out.out);
|
|
os::close(pipe_err.out);
|
|
os::close(pipe_in.out);
|
|
|
|
let stdout_po = comm::port();
|
|
let stdout_ch = comm::chan(stdout_po);
|
|
do task::spawn_sched(task::single_threaded) {
|
|
comm::send(stdout_ch, readclose(pipe_out.in));
|
|
}
|
|
let stdout = comm::recv(stdout_po);
|
|
|
|
let stderr_po = comm::port();
|
|
let stderr_ch = comm::chan(stderr_po);
|
|
do task::spawn_sched(task::single_threaded) {
|
|
comm::send(stderr_ch, readclose(pipe_err.in));
|
|
}
|
|
let stderr = comm::recv(stderr_po);
|
|
|
|
let status = run::waitpid(pid);
|
|
debug!{"pandoc result: %i", status};
|
|
if status != 0 {
|
|
error!{"pandoc-out: %s", stdout};
|
|
error!{"pandoc-err: %s", stderr};
|
|
fail ~"pandoc failed";
|
|
}
|
|
}
|
|
}
|
|
|
|
fn readclose(fd: libc::c_int) -> ~str {
|
|
// Copied from run::program_output
|
|
let file = os::fdopen(fd);
|
|
let reader = io::FILE_reader(file, false);
|
|
let mut buf = ~"";
|
|
while !reader.eof() {
|
|
let bytes = reader.read_bytes(4096u);
|
|
buf += str::from_bytes(bytes);
|
|
}
|
|
os::fclose(file);
|
|
return buf;
|
|
}
|
|
|
|
fn generic_writer(+process: fn~(markdown: ~str)) -> writer {
|
|
let ch = do task::spawn_listener |po: comm::port<writeinstr>| {
|
|
let mut markdown = ~"";
|
|
let mut keep_going = true;
|
|
while keep_going {
|
|
match comm::recv(po) {
|
|
write(s) => markdown += s,
|
|
done => keep_going = false
|
|
}
|
|
}
|
|
process(markdown);
|
|
};
|
|
|
|
fn~(+instr: writeinstr) {
|
|
comm::send(ch, instr);
|
|
}
|
|
}
|
|
|
|
fn make_local_filename(
|
|
config: config::config,
|
|
page: doc::page
|
|
) -> ~str {
|
|
let filename = make_filename(config, page);
|
|
path::connect(config.output_dir, filename)
|
|
}
|
|
|
|
fn make_filename(
|
|
config: config::config,
|
|
page: doc::page
|
|
) -> ~str {
|
|
let filename = {
|
|
match page {
|
|
doc::cratepage(doc) => {
|
|
if config.output_format == config::pandoc_html &&
|
|
config.output_style == config::doc_per_mod {
|
|
~"index"
|
|
} else {
|
|
assert doc.topmod.name() != ~"";
|
|
doc.topmod.name()
|
|
}
|
|
}
|
|
doc::itempage(doc) => {
|
|
str::connect(doc.path() + ~[doc.name()], ~"_")
|
|
}
|
|
}
|
|
};
|
|
let ext = match config.output_format {
|
|
config::markdown => ~"md",
|
|
config::pandoc_html => ~"html"
|
|
};
|
|
|
|
filename + ~"." + ext
|
|
}
|
|
|
|
#[test]
|
|
fn should_use_markdown_file_name_based_off_crate() {
|
|
let config = {
|
|
output_dir: ~"output/dir",
|
|
output_format: config::markdown,
|
|
output_style: config::doc_per_crate
|
|
with config::default_config(~"input/test.rc")
|
|
};
|
|
let doc = test::mk_doc(~"test", ~"");
|
|
let page = doc::cratepage(doc.cratedoc());
|
|
let filename = make_local_filename(config, page);
|
|
assert filename == ~"output/dir/test.md";
|
|
}
|
|
|
|
#[test]
|
|
fn should_name_html_crate_file_name_index_html_when_doc_per_mod() {
|
|
let config = {
|
|
output_dir: ~"output/dir",
|
|
output_format: config::pandoc_html,
|
|
output_style: config::doc_per_mod
|
|
with config::default_config(~"input/test.rc")
|
|
};
|
|
let doc = test::mk_doc(~"", ~"");
|
|
let page = doc::cratepage(doc.cratedoc());
|
|
let filename = make_local_filename(config, page);
|
|
assert filename == ~"output/dir/index.html";
|
|
}
|
|
|
|
#[test]
|
|
fn should_name_mod_file_names_by_path() {
|
|
let config = {
|
|
output_dir: ~"output/dir",
|
|
output_format: config::pandoc_html,
|
|
output_style: config::doc_per_mod
|
|
with config::default_config(~"input/test.rc")
|
|
};
|
|
let doc = test::mk_doc(~"", ~"mod a { mod b { } }");
|
|
let modb = doc.cratemod().mods()[0].mods()[0];
|
|
let page = doc::itempage(doc::modtag(modb));
|
|
let filename = make_local_filename(config, page);
|
|
assert filename == ~"output/dir/a_b.html";
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
fn mk_doc(name: ~str, source: ~str) -> doc::doc {
|
|
do astsrv::from_str(source) |srv| {
|
|
let doc = extract::from_srv(srv, name);
|
|
let doc = path_pass::mk_pass().f(srv, doc);
|
|
doc
|
|
}
|
|
}
|
|
}
|
|
|
|
fn write_file(path: ~str, s: ~str) {
|
|
import io::writer_util;
|
|
|
|
match io::file_writer(path, ~[io::create, io::truncate]) {
|
|
result::ok(writer) => {
|
|
writer.write_str(s);
|
|
}
|
|
result::err(e) => fail e
|
|
}
|
|
}
|
|
|
|
fn future_writer_factory(
|
|
) -> (writer_factory, comm::port<(doc::page, ~str)>) {
|
|
let markdown_po = comm::port();
|
|
let markdown_ch = comm::chan(markdown_po);
|
|
let writer_factory = fn~(page: doc::page) -> writer {
|
|
let writer_po = comm::port();
|
|
let writer_ch = comm::chan(writer_po);
|
|
do task::spawn {
|
|
let (writer, future) = future_writer();
|
|
comm::send(writer_ch, writer);
|
|
let s = future::get(&future);
|
|
comm::send(markdown_ch, (page, s));
|
|
}
|
|
comm::recv(writer_po)
|
|
};
|
|
|
|
(writer_factory, markdown_po)
|
|
}
|
|
|
|
fn future_writer() -> (writer, future::future<~str>) {
|
|
let port = comm::port();
|
|
let chan = comm::chan(port);
|
|
let writer = fn~(+instr: writeinstr) {
|
|
comm::send(chan, copy instr);
|
|
};
|
|
let future = do future::from_fn {
|
|
let mut res = ~"";
|
|
loop {
|
|
match comm::recv(port) {
|
|
write(s) => res += s,
|
|
done => break
|
|
}
|
|
}
|
|
res
|
|
};
|
|
(writer, future)
|
|
}
|