]>
Commit | Line | Data |
---|---|---|
cc61c64b | 1 | #![feature(rustc_private)] |
7453a54e | 2 | |
3dfed10e | 3 | extern crate rustc_driver; |
4b012472 | 4 | extern crate rustc_log; |
fe692bf9 | 5 | extern crate rustc_session; |
7453a54e | 6 | |
c0240ec0 FG |
7 | extern crate rustc_errors; |
8 | use rustc_errors::codes::DIAGNOSTICS; | |
9 | ||
54a0048b SL |
10 | use std::env; |
11 | use std::error::Error; | |
f2b60f7d | 12 | use std::fs::{self, File}; |
0731742a | 13 | use std::io::Write; |
7453a54e | 14 | use std::path::Path; |
54a0048b | 15 | use std::path::PathBuf; |
f2b60f7d | 16 | use std::str::FromStr; |
7453a54e | 17 | |
f2b60f7d FG |
18 | use mdbook::book::{parse_summary, BookItem, Chapter}; |
19 | use mdbook::{Config, MDBook}; | |
e1599b0c | 20 | |
7453a54e | 21 | enum OutputFormat { |
f2b60f7d FG |
22 | HTML, |
23 | Markdown, | |
7453a54e SL |
24 | Unknown(String), |
25 | } | |
26 | ||
27 | impl OutputFormat { | |
f2b60f7d | 28 | fn from(format: &str) -> OutputFormat { |
7453a54e | 29 | match &*format.to_lowercase() { |
f2b60f7d FG |
30 | "html" => OutputFormat::HTML, |
31 | "markdown" => OutputFormat::Markdown, | |
dfeec247 | 32 | s => OutputFormat::Unknown(s.to_owned()), |
7453a54e SL |
33 | } |
34 | } | |
35 | } | |
36 | ||
f2b60f7d FG |
37 | /// Output an HTML page for the errors in `err_map` to `output_path`. |
38 | fn render_markdown(output_path: &Path) -> Result<(), Box<dyn Error>> { | |
39 | let mut output_file = File::create(output_path)?; | |
7453a54e | 40 | |
f2b60f7d | 41 | write!(output_file, "# Rust Compiler Error Index\n")?; |
7453a54e | 42 | |
c0240ec0 | 43 | for (err_code, description) in DIAGNOSTICS.iter() { |
9ffffee4 | 44 | write!(output_file, "## {}\n{}\n", err_code, description)? |
7453a54e SL |
45 | } |
46 | ||
f2b60f7d FG |
47 | Ok(()) |
48 | } | |
7453a54e | 49 | |
f2b60f7d FG |
50 | // By default, mdbook doesn't consider code blocks as Rust ones contrary to rustdoc so we have |
51 | // to manually add `rust` attribute whenever needed. | |
52 | fn add_rust_attribute_on_codeblock(explanation: &str) -> String { | |
53 | // Very hacky way to add the rust attribute on all code blocks. | |
54 | let mut skip = true; | |
55 | explanation.split("\n```").fold(String::new(), |mut acc, part| { | |
56 | if !acc.is_empty() { | |
57 | acc.push_str("\n```"); | |
58 | } | |
59 | if !skip { | |
60 | if let Some(attrs) = part.split('\n').next() { | |
61 | if !attrs.contains("rust") | |
62 | && (attrs.is_empty() | |
63 | || attrs.contains("compile_fail") | |
64 | || attrs.contains("ignore") | |
65 | || attrs.contains("edition")) | |
66 | { | |
67 | if !attrs.is_empty() { | |
68 | acc.push_str("rust,"); | |
69 | } else { | |
70 | acc.push_str("rust"); | |
c295e0f8 | 71 | } |
f2b60f7d | 72 | } |
dfeec247 | 73 | } |
7453a54e | 74 | } |
f2b60f7d FG |
75 | skip = !skip; |
76 | acc.push_str(part); | |
77 | acc | |
78 | }) | |
7453a54e SL |
79 | } |
80 | ||
f2b60f7d FG |
81 | fn render_html(output_path: &Path) -> Result<(), Box<dyn Error>> { |
82 | let mut introduction = format!( | |
9c376795 | 83 | "# Rust error codes index |
f2b60f7d FG |
84 | |
85 | This page lists all the error codes emitted by the Rust compiler. | |
86 | ||
87 | " | |
88 | ); | |
89 | ||
c0240ec0 | 90 | let err_codes = DIAGNOSTICS; |
f2b60f7d FG |
91 | let mut chapters = Vec::with_capacity(err_codes.len()); |
92 | ||
93 | for (err_code, explanation) in err_codes.iter() { | |
9ffffee4 FG |
94 | introduction.push_str(&format!(" * [{0}](./{0}.html)\n", err_code)); |
95 | ||
96 | let content = add_rust_attribute_on_codeblock(explanation); | |
97 | chapters.push(BookItem::Chapter(Chapter { | |
98 | name: err_code.to_string(), | |
99 | content: format!("# Error code {}\n\n{}\n", err_code, content), | |
100 | number: None, | |
101 | sub_items: Vec::new(), | |
102 | // We generate it into the `error_codes` folder. | |
103 | path: Some(PathBuf::from(&format!("{}.html", err_code))), | |
104 | source_path: None, | |
105 | parent_names: Vec::new(), | |
106 | })); | |
7453a54e | 107 | } |
7453a54e | 108 | |
f2b60f7d FG |
109 | let mut config = Config::from_str(include_str!("book_config.toml"))?; |
110 | config.build.build_dir = output_path.join("error_codes").to_path_buf(); | |
111 | let mut book = MDBook::load_with_config_and_summary( | |
112 | env!("CARGO_MANIFEST_DIR"), | |
113 | config, | |
114 | parse_summary("")?, | |
115 | )?; | |
116 | let chapter = Chapter { | |
117 | name: "Rust error codes index".to_owned(), | |
118 | content: introduction, | |
119 | number: None, | |
120 | sub_items: chapters, | |
121 | // Very important: this file is named as `error-index.html` and not `index.html`! | |
122 | path: Some(PathBuf::from("error-index.html")), | |
123 | source_path: None, | |
124 | parent_names: Vec::new(), | |
125 | }; | |
126 | book.book.sections.push(BookItem::Chapter(chapter)); | |
127 | book.build()?; | |
128 | ||
9c376795 FG |
129 | // The error-index used to be generated manually (without mdbook), and the |
130 | // index was located at the top level. Now that it is generated with | |
131 | // mdbook, error-index.html has moved to error_codes/error-index.html. | |
132 | // This adds a redirect so that old links go to the new location. | |
133 | // | |
134 | // We can't put this content into another file, otherwise `mdbook` will also put it into the | |
f2b60f7d FG |
135 | // output directory, making a duplicate. |
136 | fs::write( | |
137 | output_path.join("error-index.html"), | |
138 | r#"<!DOCTYPE html> | |
139 | <html> | |
140 | <head> | |
141 | <title>Rust error codes index - Error codes index</title> | |
142 | <meta content="text/html; charset=utf-8" http-equiv="Content-Type"> | |
143 | <meta name="description" content="Book listing all Rust error codes"> | |
144 | <script src="error_codes/redirect.js"></script> | |
145 | </head> | |
146 | <body> | |
147 | <div>If you are not automatically redirected to the error code index, please <a id="index-link" href="./error_codes/error-index.html">here</a>. | |
f2b60f7d FG |
148 | </body> |
149 | </html>"#, | |
150 | )?; | |
151 | ||
f2b60f7d | 152 | Ok(()) |
7453a54e SL |
153 | } |
154 | ||
8faf50e0 | 155 | fn main_with_result(format: OutputFormat, dst: &Path) -> Result<(), Box<dyn Error>> { |
7453a54e | 156 | match format { |
dfeec247 | 157 | OutputFormat::Unknown(s) => panic!("Unknown output format: {}", s), |
f2b60f7d FG |
158 | OutputFormat::HTML => render_html(dst), |
159 | OutputFormat::Markdown => render_markdown(dst), | |
7453a54e | 160 | } |
7453a54e SL |
161 | } |
162 | ||
54a0048b SL |
163 | fn parse_args() -> (OutputFormat, PathBuf) { |
164 | let mut args = env::args().skip(1); | |
9fa01778 XL |
165 | let format = args.next(); |
166 | let dst = args.next(); | |
f2b60f7d | 167 | let format = format.map(|a| OutputFormat::from(&a)).unwrap_or(OutputFormat::from("html")); |
dfeec247 | 168 | let dst = dst.map(PathBuf::from).unwrap_or_else(|| match format { |
f2b60f7d FG |
169 | OutputFormat::HTML => PathBuf::from("doc"), |
170 | OutputFormat::Markdown => PathBuf::from("doc/error-index.md"), | |
dfeec247 | 171 | OutputFormat::Unknown(..) => PathBuf::from("<nul>"), |
54a0048b SL |
172 | }); |
173 | (format, dst) | |
7453a54e SL |
174 | } |
175 | ||
176 | fn main() { | |
fe692bf9 | 177 | let handler = |
4b012472 FG |
178 | rustc_session::EarlyDiagCtxt::new(rustc_session::config::ErrorOutputType::default()); |
179 | rustc_driver::init_logger(&handler, rustc_log::LoggerConfig::from_env("RUST_LOG")); | |
54a0048b | 180 | let (format, dst) = parse_args(); |
f2b60f7d | 181 | let result = main_with_result(format, &dst); |
0531ce1d | 182 | if let Err(e) = result { |
f2b60f7d | 183 | panic!("{:?}", e); |
04454e1e | 184 | } |
04454e1e | 185 | } |