rust/src/librustdoc/markdown_writer.rs

330 lines
8.7 KiB
Rust
Raw Normal View History

// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use core::prelude::*;
use config;
use config::Config;
2012-09-18 18:48:40 -05:00
use doc::ItemUtils;
use doc;
use pass::Pass;
use core::io::ReaderUtil;
use core::io;
use core::libc;
use core::oldcomm;
use core::os;
use core::pipes;
use core::result;
use core::run;
use core::str;
use core::task;
2013-02-01 20:03:32 -06:00
use core::pipes::*;
2012-10-23 16:20:57 -05:00
use std::future;
use syntax;
2012-11-19 20:00:12 -06:00
pub enum WriteInstr {
2012-09-18 18:48:40 -05:00
Write(~str),
Done
}
2013-01-30 21:32:36 -06:00
pub type Writer = fn~(v: WriteInstr);
pub type WriterFactory = fn~(page: doc::Page) -> Writer;
2012-11-19 20:00:12 -06:00
pub trait WriterUtils {
2013-01-30 21:56:17 -06:00
fn write_str(&self, +str: ~str);
fn write_line(&self, +str: ~str);
fn write_done(&self);
}
2012-09-18 18:48:40 -05:00
impl Writer: WriterUtils {
2013-01-30 21:56:17 -06:00
fn write_str(&self, str: ~str) {
(*self)(Write(str));
}
2013-01-30 21:56:17 -06:00
fn write_line(&self, str: ~str) {
self.write_str(str + ~"\n");
}
2013-01-30 21:56:17 -06:00
fn write_done(&self) {
(*self)(Done)
}
}
2013-01-30 21:32:36 -06:00
pub fn make_writer_factory(config: config::Config) -> WriterFactory {
2012-08-06 14:34:08 -05:00
match config.output_format {
2012-09-18 18:48:40 -05:00
config::Markdown => {
markdown_writer_factory(config)
2012-03-01 16:21:14 -06:00
}
2012-09-18 18:48:40 -05:00
config::PandocHtml => {
pandoc_writer_factory(config)
2012-03-01 16:21:14 -06:00
}
}
}
2013-01-30 21:32:36 -06:00
fn markdown_writer_factory(config: config::Config) -> WriterFactory {
fn~(page: doc::Page) -> Writer {
2013-01-30 15:14:35 -06:00
markdown_writer(copy config, page)
}
}
2013-01-30 21:32:36 -06:00
fn pandoc_writer_factory(config: config::Config) -> WriterFactory {
fn~(page: doc::Page) -> Writer {
2013-01-30 15:14:35 -06:00
pandoc_writer(copy config, page)
}
}
fn markdown_writer(
2013-01-30 21:32:36 -06:00
config: config::Config,
page: doc::Page
2012-09-18 18:48:40 -05:00
) -> Writer {
let filename = make_local_filename(config, page);
2012-06-30 18:19:07 -05:00
do generic_writer |markdown| {
write_file(&filename, markdown);
2012-03-01 16:21:14 -06:00
}
}
fn pandoc_writer(
2013-01-30 21:32:36 -06:00
config: config::Config,
page: doc::Page
2012-09-18 18:48:40 -05:00
) -> Writer {
2012-09-21 21:37:57 -05:00
assert config.pandoc_cmd.is_some();
2013-01-30 15:14:35 -06:00
let pandoc_cmd = (&config.pandoc_cmd).get();
let filename = make_local_filename(config, page);
2012-03-01 16:21:14 -06:00
let pandoc_args = ~[
~"--standalone",
~"--section-divs",
~"--from=markdown",
~"--to=html",
~"--css=rust.css",
~"--output=" + filename.to_str()
];
2012-03-01 16:21:14 -06:00
2012-06-30 18:19:07 -05:00
do generic_writer |markdown| {
use io::WriterUtil;
2012-03-01 16:21:14 -06:00
2012-08-22 19:24:52 -05:00
debug!("pandoc cmd: %s", pandoc_cmd);
debug!("pandoc args: %s", str::connect(pandoc_args, ~" "));
2012-03-01 16:21:14 -06:00
let pipe_in = os::pipe();
let pipe_out = os::pipe();
let pipe_err = os::pipe();
let pid = run::spawn_process(
2012-08-20 14:23:37 -05:00
pandoc_cmd, pandoc_args, &None, &None,
2012-03-01 16:21:14 -06:00
pipe_in.in, pipe_out.out, pipe_err.out);
let writer = io::fd_writer(pipe_in.out, false);
writer.write_str(markdown);
2012-03-01 16:21:14 -06:00
os::close(pipe_in.in);
os::close(pipe_out.out);
os::close(pipe_err.out);
os::close(pipe_in.out);
let (stdout_po, stdout_ch) = pipes::stream();
2012-11-28 22:34:40 -06:00
do task::spawn_sched(task::SingleThreaded) |move stdout_ch| {
stdout_ch.send(readclose(pipe_out.in));
}
let (stderr_po, stderr_ch) = pipes::stream();
2012-11-28 22:34:40 -06:00
do task::spawn_sched(task::SingleThreaded) |move stderr_ch| {
stderr_ch.send(readclose(pipe_err.in));
}
2012-11-28 22:34:40 -06:00
let stdout = stdout_po.recv();
let stderr = stderr_po.recv();
2012-03-01 16:21:14 -06:00
let status = run::waitpid(pid);
2012-08-22 19:24:52 -05:00
debug!("pandoc result: %i", status);
2012-03-01 16:21:14 -06:00
if status != 0 {
2012-08-22 19:24:52 -05:00
error!("pandoc-out: %s", stdout);
error!("pandoc-err: %s", stderr);
die!(~"pandoc failed");
2012-03-01 16:21:14 -06:00
}
}
}
fn readclose(fd: libc::c_int) -> ~str {
// Copied from run::program_output
unsafe {
let file = os::fdopen(fd);
let reader = io::FILE_reader(file, false);
let buf = io::with_bytes_writer(|writer| {
let mut bytes = [mut 0, ..4096];
while !reader.eof() {
let nread = reader.read(bytes, bytes.len());
writer.write(bytes.view(0, nread));
}
});
os::fclose(file);
str::from_bytes(buf)
}
}
2013-01-30 21:32:36 -06:00
fn generic_writer(process: fn~(markdown: ~str)) -> Writer {
2013-02-01 20:03:32 -06:00
let (po, ch) = stream::<WriteInstr>();
2012-11-28 22:34:40 -06:00
do task::spawn |move process, move setup_ch| {
let mut markdown = ~"";
let mut keep_going = true;
while keep_going {
2012-11-28 22:34:40 -06:00
match po.recv() {
2012-09-18 18:48:40 -05:00
Write(s) => markdown += s,
Done => keep_going = false
}
}
2012-09-19 00:43:54 -05:00
process(move markdown);
};
2013-01-30 21:32:36 -06:00
fn~(instr: WriteInstr) {
2013-02-01 20:03:32 -06:00
ch.send(instr);
}
}
fn make_local_filename(
2013-01-30 21:32:36 -06:00
config: config::Config,
page: doc::Page
) -> Path {
2013-01-30 15:14:35 -06:00
let filename = make_filename(copy config, page);
config.output_dir.push_rel(&filename)
}
2012-11-19 20:00:12 -06:00
pub fn make_filename(
2013-01-30 21:32:36 -06:00
config: config::Config,
page: doc::Page
) -> Path {
let filename = {
2012-08-06 14:34:08 -05:00
match page {
2012-09-18 18:48:40 -05:00
doc::CratePage(doc) => {
if config.output_format == config::PandocHtml &&
config.output_style == config::DocPerMod {
~"index"
} else {
assert doc.topmod.name() != ~"";
doc.topmod.name()
}
}
2012-09-18 18:48:40 -05:00
doc::ItemPage(doc) => {
str::connect(doc.path() + ~[doc.name()], ~"_")
}
}
};
2012-08-06 14:34:08 -05:00
let ext = match config.output_format {
2012-09-18 18:48:40 -05:00
config::Markdown => ~"md",
config::PandocHtml => ~"html"
};
Path(filename).with_filetype(ext)
}
#[test]
fn should_use_markdown_file_name_based_off_crate() {
let config = Config {
output_dir: Path("output/dir"),
2012-09-18 18:48:40 -05:00
output_format: config::Markdown,
output_style: config::DocPerCrate,
2012-09-04 15:29:32 -05:00
.. config::default_config(&Path("input/test.rc"))
};
let doc = test::mk_doc(~"test", ~"");
2012-09-18 18:48:40 -05:00
let page = doc::CratePage(doc.CrateDoc());
let filename = make_local_filename(config, page);
assert filename.to_str() == ~"output/dir/test.md";
}
#[test]
fn should_name_html_crate_file_name_index_html_when_doc_per_mod() {
let config = Config {
output_dir: Path("output/dir"),
2012-09-18 18:48:40 -05:00
output_format: config::PandocHtml,
output_style: config::DocPerMod,
2012-09-04 15:29:32 -05:00
.. config::default_config(&Path("input/test.rc"))
};
let doc = test::mk_doc(~"", ~"");
2012-09-18 18:48:40 -05:00
let page = doc::CratePage(doc.CrateDoc());
let filename = make_local_filename(config, page);
assert filename.to_str() == ~"output/dir/index.html";
}
#[test]
fn should_name_mod_file_names_by_path() {
let config = Config {
output_dir: Path("output/dir"),
2012-09-18 18:48:40 -05:00
output_format: config::PandocHtml,
output_style: config::DocPerMod,
2012-09-04 15:29:32 -05:00
.. config::default_config(&Path("input/test.rc"))
};
let doc = test::mk_doc(~"", ~"mod a { mod b { } }");
2013-01-30 15:14:35 -06:00
let modb = copy doc.cratemod().mods()[0].mods()[0];
2012-09-18 18:48:40 -05:00
let page = doc::ItemPage(doc::ModTag(modb));
let filename = make_local_filename(config, page);
assert filename == Path("output/dir/a_b.html");
}
#[cfg(test)]
mod test {
use astsrv;
use doc;
use extract;
use path_pass;
2013-01-30 21:32:36 -06:00
pub fn mk_doc(name: ~str, source: ~str) -> doc::Doc {
2012-06-30 18:19:07 -05:00
do astsrv::from_str(source) |srv| {
2013-01-31 22:24:03 -06:00
let doc = extract::from_srv(srv.clone(), copy name);
let doc = (path_pass::mk_pass().f)(srv.clone(), doc);
doc
}
}
}
2013-01-30 21:32:36 -06:00
fn write_file(path: &Path, s: ~str) {
use io::WriterUtil;
2012-08-14 15:38:35 -05:00
match io::file_writer(path, ~[io::Create, io::Truncate]) {
2012-08-26 18:54:31 -05:00
result::Ok(writer) => {
writer.write_str(s);
}
result::Err(e) => die!(e)
}
}
2012-11-19 20:00:12 -06:00
pub fn future_writer_factory(
2012-12-13 16:18:47 -06:00
) -> (WriterFactory, oldcomm::Port<(doc::Page, ~str)>) {
let markdown_po = oldcomm::Port();
let markdown_ch = oldcomm::Chan(&markdown_po);
2013-01-30 21:32:36 -06:00
let writer_factory = fn~(page: doc::Page) -> Writer {
let (writer_po, writer_ch) = pipes::stream();
2012-11-28 22:34:40 -06:00
do task::spawn |move writer_ch| {
let (writer, future) = future_writer();
2012-11-28 22:34:40 -06:00
writer_ch.send(move writer);
let s = future.get();
2013-01-30 15:14:35 -06:00
oldcomm::send(markdown_ch, (copy page, s));
}
2012-11-28 22:34:40 -06:00
writer_po.recv()
};
2012-09-19 00:43:54 -05:00
(move writer_factory, markdown_po)
}
2012-09-18 18:48:40 -05:00
fn future_writer() -> (Writer, future::Future<~str>) {
let (port, chan) = pipes::stream();
2013-01-30 21:32:36 -06:00
let writer = fn~(move chan, instr: WriteInstr) {
chan.send(copy instr);
};
2012-09-19 00:43:54 -05:00
let future = do future::from_fn |move port| {
let mut res = ~"";
loop {
match port.recv() {
2012-09-18 18:48:40 -05:00
Write(s) => res += s,
Done => break
}
}
res
};
2012-09-19 00:43:54 -05:00
(move writer, move future)
}