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