]>
Commit | Line | Data |
---|---|---|
1 | #![feature(rustc_private)] | |
2 | ||
3 | extern crate rustc_driver; | |
4 | extern crate rustc_log; | |
5 | extern crate rustc_session; | |
6 | ||
7 | extern crate rustc_errors; | |
8 | use rustc_errors::codes::DIAGNOSTICS; | |
9 | ||
10 | use std::env; | |
11 | use std::error::Error; | |
12 | use std::fs::{self, File}; | |
13 | use std::io::Write; | |
14 | use std::path::Path; | |
15 | use std::path::PathBuf; | |
16 | use std::str::FromStr; | |
17 | ||
18 | use mdbook::book::{parse_summary, BookItem, Chapter}; | |
19 | use mdbook::{Config, MDBook}; | |
20 | ||
21 | enum OutputFormat { | |
22 | HTML, | |
23 | Markdown, | |
24 | Unknown(String), | |
25 | } | |
26 | ||
27 | impl OutputFormat { | |
28 | fn from(format: &str) -> OutputFormat { | |
29 | match &*format.to_lowercase() { | |
30 | "html" => OutputFormat::HTML, | |
31 | "markdown" => OutputFormat::Markdown, | |
32 | s => OutputFormat::Unknown(s.to_owned()), | |
33 | } | |
34 | } | |
35 | } | |
36 | ||
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)?; | |
40 | ||
41 | write!(output_file, "# Rust Compiler Error Index\n")?; | |
42 | ||
43 | for (err_code, description) in DIAGNOSTICS.iter() { | |
44 | write!(output_file, "## {}\n{}\n", err_code, description)? | |
45 | } | |
46 | ||
47 | Ok(()) | |
48 | } | |
49 | ||
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"); | |
71 | } | |
72 | } | |
73 | } | |
74 | } | |
75 | skip = !skip; | |
76 | acc.push_str(part); | |
77 | acc | |
78 | }) | |
79 | } | |
80 | ||
81 | fn render_html(output_path: &Path) -> Result<(), Box<dyn Error>> { | |
82 | let mut introduction = format!( | |
83 | "# Rust error codes index | |
84 | ||
85 | This page lists all the error codes emitted by the Rust compiler. | |
86 | ||
87 | " | |
88 | ); | |
89 | ||
90 | let err_codes = DIAGNOSTICS; | |
91 | let mut chapters = Vec::with_capacity(err_codes.len()); | |
92 | ||
93 | for (err_code, explanation) in err_codes.iter() { | |
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 | })); | |
107 | } | |
108 | ||
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 | ||
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 | |
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>. | |
148 | </body> | |
149 | </html>"#, | |
150 | )?; | |
151 | ||
152 | Ok(()) | |
153 | } | |
154 | ||
155 | fn main_with_result(format: OutputFormat, dst: &Path) -> Result<(), Box<dyn Error>> { | |
156 | match format { | |
157 | OutputFormat::Unknown(s) => panic!("Unknown output format: {}", s), | |
158 | OutputFormat::HTML => render_html(dst), | |
159 | OutputFormat::Markdown => render_markdown(dst), | |
160 | } | |
161 | } | |
162 | ||
163 | fn parse_args() -> (OutputFormat, PathBuf) { | |
164 | let mut args = env::args().skip(1); | |
165 | let format = args.next(); | |
166 | let dst = args.next(); | |
167 | let format = format.map(|a| OutputFormat::from(&a)).unwrap_or(OutputFormat::from("html")); | |
168 | let dst = dst.map(PathBuf::from).unwrap_or_else(|| match format { | |
169 | OutputFormat::HTML => PathBuf::from("doc"), | |
170 | OutputFormat::Markdown => PathBuf::from("doc/error-index.md"), | |
171 | OutputFormat::Unknown(..) => PathBuf::from("<nul>"), | |
172 | }); | |
173 | (format, dst) | |
174 | } | |
175 | ||
176 | fn main() { | |
177 | let handler = | |
178 | rustc_session::EarlyDiagCtxt::new(rustc_session::config::ErrorOutputType::default()); | |
179 | rustc_driver::init_logger(&handler, rustc_log::LoggerConfig::from_env("RUST_LOG")); | |
180 | let (format, dst) = parse_args(); | |
181 | let result = main_with_result(format, &dst); | |
182 | if let Err(e) = result { | |
183 | panic!("{:?}", e); | |
184 | } | |
185 | } |