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.
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.
11 #![feature(rustc_private)]
13 extern crate env_logger;
16 extern crate serialize as rustc_serialize;
18 use std::collections::BTreeMap;
20 use std::error::Error;
21 use std::fs::{read_dir, File};
22 use std::io::{Read, Write};
24 use std::path::PathBuf;
25 use std::cell::RefCell;
27 use syntax::diagnostics::metadata::{get_metadata_dir, ErrorMetadataMap, ErrorMetadata};
29 use rustdoc::html::markdown::{Markdown, IdMap, ErrorCodes, PLAYGROUND};
30 use rustc_serialize::json;
34 Markdown(MarkdownFormatter),
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()),
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>>;
56 struct HTMLFormatter(RefCell<IdMap>);
57 struct MarkdownFormatter;
59 impl Formatter for HTMLFormatter {
60 fn header(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
61 write!(output, r##"<!DOCTYPE html>
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"/>
80 fn title(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
81 write!(output, "<h1>Rust Compiler Error Index</h1>\n")?;
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",
92 let use_desc = match info.use_site {
93 Some(_) => "error-used",
94 None => "error-unused",
96 write!(output, "<div class=\"{} {}\">", desc_desc, use_desc)?;
98 // Error title (with self-link).
100 "<h2 id=\"{0}\" class=\"section-header\"><a href=\"#{0}\">{0}</a></h2>\n",
103 // Description rendered as markdown.
104 match info.description {
106 let mut id_map = self.0.borrow_mut();
108 Markdown(desc, &[], RefCell::new(&mut id_map), ErrorCodes::Yes))?
110 None => write!(output, "<p>No description.</p>\n")?,
113 write!(output, "</div>\n")?;
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++) {{
127 function hasClass(elem, className) {{
128 if (elem && className && elem.className) {{
129 var elemClass = elem.className;
130 var start = elemClass.indexOf(className);
133 }} else if (elemClass.length === className.length) {{
136 if (start > 0 && elemClass[start - 1] !== ' ') {{
139 var end = start + className.length;
140 if (end < elemClass.length && elemClass[end] !== ' ') {{
145 if (start > 0 && elemClass[start - 1] !== ' ') {{
148 var end = start + className.length;
149 if (end < elemClass.length && elemClass[end] !== ' ') {{
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';
162 e.addEventListener("mouseout", function(event) {{
163 e.previousElementSibling.childNodes[0].style.color = '';
165 }} else if (hasClass(e, 'ignore')) {{
166 e.addEventListener("mouseover", function(event) {{
167 e.previousElementSibling.childNodes[0].style.color = '#ff9200';
169 e.addEventListener("mouseout", function(event) {{
170 e.previousElementSibling.childNodes[0].style.color = '';
181 impl Formatter for MarkdownFormatter {
182 #[allow(unused_variables)]
183 fn header(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
187 fn title(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
188 write!(output, "# Rust Compiler Error Index\n")?;
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)?,
200 #[allow(unused_variables)]
201 fn footer(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
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();
210 for entry in read_dir(metadata_dir)? {
211 let path = entry?.path();
213 let mut metadata_str = String::new();
214 File::open(&path).and_then(|mut f| f.read_to_string(&mut metadata_str))?;
216 let some_errors: ErrorMetadataMap = json::decode(&metadata_str)?;
218 for (err_code, info) in some_errors {
219 all_errors.insert(err_code, info);
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)?;
231 formatter.header(&mut output_file)?;
232 formatter.title(&mut output_file)?;
234 for (err_code, info) in err_map {
235 formatter.error_code_block(&mut output_file, info, err_code)?;
238 formatter.footer(&mut output_file)
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)?;
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)?,
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(|| {
259 OutputFormat::HTML(..) => PathBuf::from("doc/error-index.html"),
260 OutputFormat::Markdown(..) => PathBuf::from("doc/error-index.md"),
261 OutputFormat::Unknown(..) => PathBuf::from("<nul>"),
269 PLAYGROUND.with(|slot| {
270 *slot.borrow_mut() = Some((None, String::from("https://play.rust-lang.org/")));
272 let (format, dst) = parse_args();
273 let result = syntax::with_globals(move || {
274 main_with_result(format, &dst)
276 if let Err(e) = result {
277 panic!("{}", e.description());