]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/markdown_writer.rs
Revert "auto merge of #8645 : alexcrichton/rust/issue-6436-run-non-blocking, r=brson"
[rust.git] / src / librustdoc / markdown_writer.rs
1 // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11
12 use config;
13 use doc::ItemUtils;
14 use doc;
15
16 use std::comm::*;
17 use std::comm;
18 use std::io;
19 use std::result;
20 use std::run;
21 use std::str;
22 use std::task;
23 use extra::future;
24
25 #[deriving(Clone)]
26 pub enum WriteInstr {
27     Write(~str),
28     Done
29 }
30
31 pub type Writer = ~fn(v: WriteInstr);
32 pub type WriterFactory = ~fn(page: doc::Page) -> Writer;
33
34 pub trait WriterUtils {
35     fn put_str(&self, str: ~str);
36     fn put_line(&self, str: ~str);
37     fn put_done(&self);
38 }
39
40 impl WriterUtils for Writer {
41     fn put_str(&self, str: ~str) {
42         (*self)(Write(str));
43     }
44
45     fn put_line(&self, str: ~str) {
46         self.put_str(str + "\n");
47     }
48
49     fn put_done(&self) {
50         (*self)(Done)
51     }
52 }
53
54 pub fn make_writer_factory(config: config::Config) -> WriterFactory {
55     match config.output_format {
56       config::Markdown => {
57         markdown_writer_factory(config)
58       }
59       config::PandocHtml => {
60         pandoc_writer_factory(config)
61       }
62     }
63 }
64
65 fn markdown_writer_factory(config: config::Config) -> WriterFactory {
66     let result: ~fn(page: doc::Page) -> Writer = |page| {
67         markdown_writer(&config, page)
68     };
69     result
70 }
71
72 fn pandoc_writer_factory(config: config::Config) -> WriterFactory {
73     let result: ~fn(doc::Page) -> Writer = |page| {
74         pandoc_writer(&config, page)
75     };
76     result
77 }
78
79 fn markdown_writer(
80     config: &config::Config,
81     page: doc::Page
82 ) -> Writer {
83     let filename = make_local_filename(config, page);
84     do generic_writer |markdown| {
85         write_file(&filename, markdown);
86     }
87 }
88
89 fn pandoc_writer(
90     config: &config::Config,
91     page: doc::Page
92 ) -> Writer {
93     assert!(config.pandoc_cmd.is_some());
94     let pandoc_cmd = (*config.pandoc_cmd.get_ref()).clone();
95     let filename = make_local_filename(config, page);
96
97     let pandoc_args = ~[
98         ~"--standalone",
99         ~"--section-divs",
100         ~"--from=markdown",
101         ~"--to=html",
102         ~"--css=rust.css",
103         ~"--output=" + filename.to_str()
104     ];
105
106     do generic_writer |markdown| {
107         use std::io::WriterUtil;
108
109         debug!("pandoc cmd: %s", pandoc_cmd);
110         debug!("pandoc args: %s", pandoc_args.connect(" "));
111
112         let mut proc = run::Process::new(pandoc_cmd, pandoc_args, run::ProcessOptions::new());
113
114         proc.input().write_str(markdown);
115         let output = proc.finish_with_output();
116
117         debug!("pandoc result: %i", output.status);
118         if output.status != 0 {
119             error!("pandoc-out: %s", str::from_bytes(output.output));
120             error!("pandoc-err: %s", str::from_bytes(output.error));
121             fail!("pandoc failed");
122         }
123     }
124 }
125
126 fn generic_writer(process: ~fn(markdown: ~str)) -> Writer {
127     let (po, ch) = stream::<WriteInstr>();
128     do task::spawn || {
129         let mut markdown = ~"";
130         let mut keep_going = true;
131         while keep_going {
132             match po.recv() {
133               Write(s) => markdown.push_str(s),
134               Done => keep_going = false
135             }
136         }
137         process(markdown);
138     };
139     let result: ~fn(instr: WriteInstr) = |instr| ch.send(instr);
140     result
141 }
142
143 pub fn make_local_filename(
144     config: &config::Config,
145     page: doc::Page
146 ) -> Path {
147     let filename = make_filename(config, page);
148     config.output_dir.push_rel(&filename)
149 }
150
151 pub fn make_filename(
152     config: &config::Config,
153     page: doc::Page
154 ) -> Path {
155     let filename = {
156         match page {
157           doc::CratePage(doc) => {
158             if config.output_format == config::PandocHtml &&
159                 config.output_style == config::DocPerMod {
160                 ~"index"
161             } else {
162                 assert!(doc.topmod.name_() != ~"");
163                 doc.topmod.name_()
164             }
165           }
166           doc::ItemPage(doc) => {
167             (doc.path() + &[doc.name_()]).connect("_")
168           }
169         }
170     };
171     let ext = match config.output_format {
172       config::Markdown => ~"md",
173       config::PandocHtml => ~"html"
174     };
175
176     Path(filename).with_filetype(ext)
177 }
178
179 fn write_file(path: &Path, s: ~str) {
180     use std::io::WriterUtil;
181
182     match io::file_writer(path, [io::Create, io::Truncate]) {
183       result::Ok(writer) => {
184         writer.write_str(s);
185       }
186       result::Err(e) => fail!(e)
187     }
188 }
189
190 pub fn future_writer_factory(
191 ) -> (WriterFactory, Port<(doc::Page, ~str)>) {
192     let (markdown_po, markdown_ch) = stream();
193     let markdown_ch = SharedChan::new(markdown_ch);
194     let writer_factory: WriterFactory = |page| {
195         let (writer_po, writer_ch) = comm::stream();
196         let markdown_ch = markdown_ch.clone();
197         do task::spawn || {
198             let (writer, future) = future_writer();
199             let mut future = future;
200             writer_ch.send(writer);
201             let s = future.get();
202             markdown_ch.send((page.clone(), s));
203         }
204         writer_po.recv()
205     };
206
207     (writer_factory, markdown_po)
208 }
209
210 fn future_writer() -> (Writer, future::Future<~str>) {
211     let (port, chan) = comm::stream();
212     let writer: ~fn(instr: WriteInstr) = |instr| chan.send(instr.clone());
213     let future = do future::from_fn || {
214         let mut res = ~"";
215         loop {
216             match port.recv() {
217               Write(s) => res.push_str(s),
218               Done => break
219             }
220         }
221         res
222     };
223     (writer, future)
224 }
225
226 #[cfg(test)]
227 mod test {
228
229     use astsrv;
230     use doc;
231     use extract;
232     use path_pass;
233     use config;
234     use super::make_local_filename;
235
236     fn mk_doc(name: ~str, source: ~str) -> doc::Doc {
237         do astsrv::from_str(source) |srv| {
238             let doc = extract::from_srv(srv.clone(), name.clone());
239             let doc = (path_pass::mk_pass().f)(srv.clone(), doc);
240             doc
241         }
242     }
243
244     #[test]
245     fn should_use_markdown_file_name_based_off_crate() {
246         let config = config::Config {
247             output_dir: Path("output/dir"),
248             output_format: config::Markdown,
249             output_style: config::DocPerCrate,
250             .. config::default_config(&Path("input/test.rc"))
251         };
252         let doc = mk_doc(~"test", ~"");
253         let page = doc::CratePage(doc.CrateDoc());
254         let filename = make_local_filename(&config, page);
255         assert_eq!(filename.to_str(), ~"output/dir/test.md");
256     }
257
258     #[test]
259     fn should_name_html_crate_file_name_index_html_when_doc_per_mod() {
260         let config = config::Config {
261             output_dir: Path("output/dir"),
262             output_format: config::PandocHtml,
263             output_style: config::DocPerMod,
264             .. config::default_config(&Path("input/test.rc"))
265         };
266         let doc = mk_doc(~"", ~"");
267         let page = doc::CratePage(doc.CrateDoc());
268         let filename = make_local_filename(&config, page);
269         assert_eq!(filename.to_str(), ~"output/dir/index.html");
270     }
271
272     #[test]
273     fn should_name_mod_file_names_by_path() {
274         let config = config::Config {
275             output_dir: Path("output/dir"),
276             output_format: config::PandocHtml,
277             output_style: config::DocPerMod,
278             .. config::default_config(&Path("input/test.rc"))
279         };
280         let doc = mk_doc(~"", ~"mod a { mod b { } }");
281         // hidden __std_macros module at the start.
282         let modb = doc.cratemod().mods()[1].mods()[0].clone();
283         let page = doc::ItemPage(doc::ModTag(modb));
284         let filename = make_local_filename(&config, page);
285         assert_eq!(filename, Path("output/dir/a_b.html"));
286     }
287 }