]> git.lizzy.rs Git - rust.git/blob - src/tools/error_index_generator/main.rs
Rollup merge of #41135 - japaric:unstable-docs, r=steveklabnik
[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 syntax;
14 extern crate rustdoc;
15 extern crate serialize as rustc_serialize;
16
17 use std::collections::BTreeMap;
18 use std::env;
19 use std::error::Error;
20 use std::fs::{read_dir, File};
21 use std::io::{Read, Write};
22 use std::path::Path;
23 use std::path::PathBuf;
24
25 use syntax::diagnostics::metadata::{get_metadata_dir, ErrorMetadataMap, ErrorMetadata};
26
27 use rustdoc::html::markdown::{Markdown, PLAYGROUND};
28 use rustc_serialize::json;
29
30 enum OutputFormat {
31     HTML(HTMLFormatter),
32     Markdown(MarkdownFormatter),
33     Unknown(String),
34 }
35
36 impl OutputFormat {
37     fn from(format: &str) -> OutputFormat {
38         match &*format.to_lowercase() {
39             "html"     => OutputFormat::HTML(HTMLFormatter),
40             "markdown" => OutputFormat::Markdown(MarkdownFormatter),
41             s          => OutputFormat::Unknown(s.to_owned()),
42         }
43     }
44 }
45
46 trait Formatter {
47     fn header(&self, output: &mut Write) -> Result<(), Box<Error>>;
48     fn title(&self, output: &mut Write) -> Result<(), Box<Error>>;
49     fn error_code_block(&self, output: &mut Write, info: &ErrorMetadata,
50                         err_code: &str) -> Result<(), Box<Error>>;
51     fn footer(&self, output: &mut Write) -> Result<(), Box<Error>>;
52 }
53
54 struct HTMLFormatter;
55 struct MarkdownFormatter;
56
57 impl Formatter for HTMLFormatter {
58     fn header(&self, output: &mut Write) -> Result<(), Box<Error>> {
59         write!(output, r##"<!DOCTYPE html>
60 <html>
61 <head>
62 <title>Rust Compiler Error Index</title>
63 <meta charset="utf-8">
64 <!-- Include rust.css after main.css so its rules take priority. -->
65 <link rel="stylesheet" type="text/css" href="main.css"/>
66 <link rel="stylesheet" type="text/css" href="rust.css"/>
67 <style>
68 .error-undescribed {{
69     display: none;
70 }}
71 </style>
72 </head>
73 <body>
74 "##)?;
75         Ok(())
76     }
77
78     fn title(&self, output: &mut Write) -> Result<(), Box<Error>> {
79         write!(output, "<h1>Rust Compiler Error Index</h1>\n")?;
80         Ok(())
81     }
82
83     fn error_code_block(&self, output: &mut Write, info: &ErrorMetadata,
84                         err_code: &str) -> Result<(), Box<Error>> {
85         // Enclose each error in a div so they can be shown/hidden en masse.
86         let desc_desc = match info.description {
87             Some(_) => "error-described",
88             None => "error-undescribed",
89         };
90         let use_desc = match info.use_site {
91             Some(_) => "error-used",
92             None => "error-unused",
93         };
94         write!(output, "<div class=\"{} {}\">", desc_desc, use_desc)?;
95
96         // Error title (with self-link).
97         write!(output,
98                "<h2 id=\"{0}\" class=\"section-header\"><a href=\"#{0}\">{0}</a></h2>\n",
99                err_code)?;
100
101         // Description rendered as markdown.
102         match info.description {
103             Some(ref desc) => write!(output, "{}", Markdown(desc))?,
104             None => write!(output, "<p>No description.</p>\n")?,
105         }
106
107         write!(output, "</div>\n")?;
108         Ok(())
109     }
110
111     fn footer(&self, output: &mut Write) -> Result<(), Box<Error>> {
112         write!(output, "</body>\n</html>")?;
113         Ok(())
114     }
115 }
116
117 impl Formatter for MarkdownFormatter {
118     #[allow(unused_variables)]
119     fn header(&self, output: &mut Write) -> Result<(), Box<Error>> {
120         Ok(())
121     }
122
123     fn title(&self, output: &mut Write) -> Result<(), Box<Error>> {
124         write!(output, "# Rust Compiler Error Index\n")?;
125         Ok(())
126     }
127
128     fn error_code_block(&self, output: &mut Write, info: &ErrorMetadata,
129                         err_code: &str) -> Result<(), Box<Error>> {
130         Ok(match info.description {
131             Some(ref desc) => write!(output, "## {}\n{}\n", err_code, desc)?,
132             None => (),
133         })
134     }
135
136     #[allow(unused_variables)]
137     fn footer(&self, output: &mut Write) -> Result<(), Box<Error>> {
138         Ok(())
139     }
140 }
141
142 /// Load all the metadata files from `metadata_dir` into an in-memory map.
143 fn load_all_errors(metadata_dir: &Path) -> Result<ErrorMetadataMap, Box<Error>> {
144     let mut all_errors = BTreeMap::new();
145
146     for entry in read_dir(metadata_dir)? {
147         let path = entry?.path();
148
149         let mut metadata_str = String::new();
150         File::open(&path).and_then(|mut f| f.read_to_string(&mut metadata_str))?;
151
152         let some_errors: ErrorMetadataMap = json::decode(&metadata_str)?;
153
154         for (err_code, info) in some_errors {
155             all_errors.insert(err_code, info);
156         }
157     }
158
159     Ok(all_errors)
160 }
161
162 /// Output an HTML page for the errors in `err_map` to `output_path`.
163 fn render_error_page<T: Formatter>(err_map: &ErrorMetadataMap, output_path: &Path,
164                                    formatter: T) -> Result<(), Box<Error>> {
165     let mut output_file = File::create(output_path)?;
166
167     formatter.header(&mut output_file)?;
168     formatter.title(&mut output_file)?;
169
170     for (err_code, info) in err_map {
171         formatter.error_code_block(&mut output_file, info, err_code)?;
172     }
173
174     formatter.footer(&mut output_file)
175 }
176
177 fn main_with_result(format: OutputFormat, dst: &Path) -> Result<(), Box<Error>> {
178     let build_arch = env::var("CFG_BUILD")?;
179     let metadata_dir = get_metadata_dir(&build_arch);
180     let err_map = load_all_errors(&metadata_dir)?;
181     match format {
182         OutputFormat::Unknown(s)  => panic!("Unknown output format: {}", s),
183         OutputFormat::HTML(h)     => render_error_page(&err_map, dst, h)?,
184         OutputFormat::Markdown(m) => render_error_page(&err_map, dst, m)?,
185     }
186     Ok(())
187 }
188
189 fn parse_args() -> (OutputFormat, PathBuf) {
190     let mut args = env::args().skip(1);
191     let format = args.next().map(|a| OutputFormat::from(&a))
192                             .unwrap_or(OutputFormat::from("html"));
193     let dst = args.next().map(PathBuf::from).unwrap_or_else(|| {
194         match format {
195             OutputFormat::HTML(..) => PathBuf::from("doc/error-index.html"),
196             OutputFormat::Markdown(..) => PathBuf::from("doc/error-index.md"),
197             OutputFormat::Unknown(..) => PathBuf::from("<nul>"),
198         }
199     });
200     (format, dst)
201 }
202
203 fn main() {
204     PLAYGROUND.with(|slot| {
205         *slot.borrow_mut() = Some((None, String::from("https://play.rust-lang.org/")));
206     });
207     let (format, dst) = parse_args();
208     if let Err(e) = main_with_result(format, &dst) {
209         panic!("{}", e.description());
210     }
211 }