]> git.lizzy.rs Git - rust.git/blob - src/tools/error_index_generator/main.rs
a5556e1d570eb89e6f04ffb258bd8f1f7ff9bfc8
[rust.git] / src / tools / error_index_generator / main.rs
1 // Copyright 2015 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 #![feature(rustc_private)]
12
13 extern crate env_logger;
14 extern crate syntax;
15 extern crate rustdoc;
16 extern crate serialize as rustc_serialize;
17
18 use std::collections::BTreeMap;
19 use std::env;
20 use std::error::Error;
21 use std::fs::{read_dir, File};
22 use std::io::{Read, Write};
23 use std::path::Path;
24 use std::path::PathBuf;
25 use std::cell::RefCell;
26
27 use syntax::diagnostics::metadata::{get_metadata_dir, ErrorMetadataMap, ErrorMetadata};
28
29 use rustdoc::html::markdown::{Markdown, IdMap, ErrorCodes, PLAYGROUND};
30 use rustc_serialize::json;
31
32 enum OutputFormat {
33     HTML(HTMLFormatter),
34     Markdown(MarkdownFormatter),
35     Unknown(String),
36 }
37
38 impl OutputFormat {
39     fn from(format: &str) -> OutputFormat {
40         match &*format.to_lowercase() {
41             "html"     => OutputFormat::HTML(HTMLFormatter(RefCell::new(IdMap::new()))),
42             "markdown" => OutputFormat::Markdown(MarkdownFormatter),
43             s          => OutputFormat::Unknown(s.to_owned()),
44         }
45     }
46 }
47
48 trait Formatter {
49     fn header(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>>;
50     fn title(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>>;
51     fn error_code_block(&self, output: &mut dyn Write, info: &ErrorMetadata,
52                         err_code: &str) -> Result<(), Box<dyn Error>>;
53     fn footer(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>>;
54 }
55
56 struct HTMLFormatter(RefCell<IdMap>);
57 struct MarkdownFormatter;
58
59 impl Formatter for HTMLFormatter {
60     fn header(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
61         write!(output, r##"<!DOCTYPE html>
62 <html>
63 <head>
64 <title>Rust Compiler Error Index</title>
65 <meta charset="utf-8">
66 <!-- Include rust.css after light.css so its rules take priority. -->
67 <link rel="stylesheet" type="text/css" href="light.css"/>
68 <link rel="stylesheet" type="text/css" href="rust.css"/>
69 <style>
70 .error-undescribed {{
71     display: none;
72 }}
73 </style>
74 </head>
75 <body>
76 "##)?;
77         Ok(())
78     }
79
80     fn title(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
81         write!(output, "<h1>Rust Compiler Error Index</h1>\n")?;
82         Ok(())
83     }
84
85     fn error_code_block(&self, output: &mut dyn Write, info: &ErrorMetadata,
86                         err_code: &str) -> Result<(), Box<dyn Error>> {
87         // Enclose each error in a div so they can be shown/hidden en masse.
88         let desc_desc = match info.description {
89             Some(_) => "error-described",
90             None => "error-undescribed",
91         };
92         let use_desc = match info.use_site {
93             Some(_) => "error-used",
94             None => "error-unused",
95         };
96         write!(output, "<div class=\"{} {}\">", desc_desc, use_desc)?;
97
98         // Error title (with self-link).
99         write!(output,
100                "<h2 id=\"{0}\" class=\"section-header\"><a href=\"#{0}\">{0}</a></h2>\n",
101                err_code)?;
102
103         // Description rendered as markdown.
104         match info.description {
105             Some(ref desc) => {
106                 let mut id_map = self.0.borrow_mut();
107                 write!(output, "{}",
108                     Markdown(desc, &[], RefCell::new(&mut id_map), ErrorCodes::Yes))?
109             },
110             None => write!(output, "<p>No description.</p>\n")?,
111         }
112
113         write!(output, "</div>\n")?;
114         Ok(())
115     }
116
117     fn footer(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
118         write!(output, r##"<script>
119 function onEach(arr, func) {{
120     if (arr && arr.length > 0 && func) {{
121         for (var i = 0; i < arr.length; i++) {{
122             func(arr[i]);
123         }}
124     }}
125 }}
126
127 function hasClass(elem, className) {{
128     if (elem && className && elem.className) {{
129         var elemClass = elem.className;
130         var start = elemClass.indexOf(className);
131         if (start === -1) {{
132             return false;
133         }} else if (elemClass.length === className.length) {{
134             return true;
135         }} else {{
136             if (start > 0 && elemClass[start - 1] !== ' ') {{
137                 return false;
138             }}
139             var end = start + className.length;
140             if (end < elemClass.length && elemClass[end] !== ' ') {{
141                 return false;
142             }}
143             return true;
144         }}
145         if (start > 0 && elemClass[start - 1] !== ' ') {{
146             return false;
147         }}
148         var end = start + className.length;
149         if (end < elemClass.length && elemClass[end] !== ' ') {{
150             return false;
151         }}
152         return true;
153     }}
154     return false;
155 }}
156
157 onEach(document.getElementsByClassName('rust-example-rendered'), function(e) {{
158     if (hasClass(e, 'compile_fail')) {{
159         e.addEventListener("mouseover", function(event) {{
160             e.previousElementSibling.childNodes[0].style.color = '#f00';
161         }});
162         e.addEventListener("mouseout", function(event) {{
163             e.previousElementSibling.childNodes[0].style.color = '';
164         }});
165     }} else if (hasClass(e, 'ignore')) {{
166         e.addEventListener("mouseover", function(event) {{
167             e.previousElementSibling.childNodes[0].style.color = '#ff9200';
168         }});
169         e.addEventListener("mouseout", function(event) {{
170             e.previousElementSibling.childNodes[0].style.color = '';
171         }});
172     }}
173 }});
174 </script>
175 </body>
176 </html>"##)?;
177         Ok(())
178     }
179 }
180
181 impl Formatter for MarkdownFormatter {
182     #[allow(unused_variables)]
183     fn header(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
184         Ok(())
185     }
186
187     fn title(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
188         write!(output, "# Rust Compiler Error Index\n")?;
189         Ok(())
190     }
191
192     fn error_code_block(&self, output: &mut dyn Write, info: &ErrorMetadata,
193                         err_code: &str) -> Result<(), Box<dyn Error>> {
194         Ok(match info.description {
195             Some(ref desc) => write!(output, "## {}\n{}\n", err_code, desc)?,
196             None => (),
197         })
198     }
199
200     #[allow(unused_variables)]
201     fn footer(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
202         Ok(())
203     }
204 }
205
206 /// Load all the metadata files from `metadata_dir` into an in-memory map.
207 fn load_all_errors(metadata_dir: &Path) -> Result<ErrorMetadataMap, Box<dyn Error>> {
208     let mut all_errors = BTreeMap::new();
209
210     for entry in read_dir(metadata_dir)? {
211         let path = entry?.path();
212
213         let mut metadata_str = String::new();
214         File::open(&path).and_then(|mut f| f.read_to_string(&mut metadata_str))?;
215
216         let some_errors: ErrorMetadataMap = json::decode(&metadata_str)?;
217
218         for (err_code, info) in some_errors {
219             all_errors.insert(err_code, info);
220         }
221     }
222
223     Ok(all_errors)
224 }
225
226 /// Output an HTML page for the errors in `err_map` to `output_path`.
227 fn render_error_page<T: Formatter>(err_map: &ErrorMetadataMap, output_path: &Path,
228                                    formatter: T) -> Result<(), Box<dyn Error>> {
229     let mut output_file = File::create(output_path)?;
230
231     formatter.header(&mut output_file)?;
232     formatter.title(&mut output_file)?;
233
234     for (err_code, info) in err_map {
235         formatter.error_code_block(&mut output_file, info, err_code)?;
236     }
237
238     formatter.footer(&mut output_file)
239 }
240
241 fn main_with_result(format: OutputFormat, dst: &Path) -> Result<(), Box<dyn Error>> {
242     let build_arch = env::var("CFG_BUILD")?;
243     let metadata_dir = get_metadata_dir(&build_arch);
244     let err_map = load_all_errors(&metadata_dir)?;
245     match format {
246         OutputFormat::Unknown(s)  => panic!("Unknown output format: {}", s),
247         OutputFormat::HTML(h)     => render_error_page(&err_map, dst, h)?,
248         OutputFormat::Markdown(m) => render_error_page(&err_map, dst, m)?,
249     }
250     Ok(())
251 }
252
253 fn parse_args() -> (OutputFormat, PathBuf) {
254     let mut args = env::args().skip(1);
255     let format = args.next().map(|a| OutputFormat::from(&a))
256                             .unwrap_or(OutputFormat::from("html"));
257     let dst = args.next().map(PathBuf::from).unwrap_or_else(|| {
258         match format {
259             OutputFormat::HTML(..) => PathBuf::from("doc/error-index.html"),
260             OutputFormat::Markdown(..) => PathBuf::from("doc/error-index.md"),
261             OutputFormat::Unknown(..) => PathBuf::from("<nul>"),
262         }
263     });
264     (format, dst)
265 }
266
267 fn main() {
268     env_logger::init();
269     PLAYGROUND.with(|slot| {
270         *slot.borrow_mut() = Some((None, String::from("https://play.rust-lang.org/")));
271     });
272     let (format, dst) = parse_args();
273     let result = syntax::with_globals(move || {
274         main_with_result(format, &dst)
275     });
276     if let Err(e) = result {
277         panic!("{}", e.description());
278     }
279 }