#![feature(rustc_private)]
extern crate rustc_driver;
-extern crate rustc_span;
+// We use the function we generate from `register_diagnostics!`.
use crate::error_codes::error_codes;
use std::env;
use std::error::Error;
-use std::fs::{create_dir_all, File};
+use std::fs::{self, File};
use std::io::Write;
use std::path::Path;
use std::path::PathBuf;
-use rustc_span::edition::DEFAULT_EDITION;
+use std::str::FromStr;
-use rustdoc::html::markdown::{ErrorCodes, HeadingOffset, IdMap, Markdown, Playground};
+use mdbook::book::{parse_summary, BookItem, Chapter};
+use mdbook::{Config, MDBook};
macro_rules! register_diagnostics {
($($error_code:ident: $message:expr,)+ ; $($undocumented:ident,)* ) => {
mod error_codes;
enum OutputFormat {
- HTML(HTMLFormatter),
+ HTML,
Markdown,
Unknown(String),
}
impl OutputFormat {
- fn from(format: &str, resource_suffix: &str) -> OutputFormat {
+ fn from(format: &str) -> OutputFormat {
match &*format.to_lowercase() {
- "html" => OutputFormat::HTML(HTMLFormatter(resource_suffix.to_owned())),
+ "html" => OutputFormat::HTML,
"markdown" => OutputFormat::Markdown,
s => OutputFormat::Unknown(s.to_owned()),
}
}
}
-struct HTMLFormatter(String);
-
-impl HTMLFormatter {
- fn create_error_code_file(
- &self,
- err_code: &str,
- explanation: &str,
- parent_dir: &Path,
- ) -> Result<(), Box<dyn Error>> {
- let mut output_file = File::create(parent_dir.join(err_code).with_extension("html"))?;
-
- self.header(&mut output_file, "../", "")?;
- self.title(&mut output_file, &format!("Error code {}", err_code))?;
-
- let mut id_map = IdMap::new();
- let playground =
- Playground { crate_name: None, url: String::from("https://play.rust-lang.org/") };
- write!(
- output_file,
- "{}",
- Markdown {
- content: explanation,
- links: &[],
- ids: &mut id_map,
- error_codes: ErrorCodes::Yes,
- edition: DEFAULT_EDITION,
- playground: &Some(playground),
- heading_offset: HeadingOffset::H1,
- }
- .into_string()
- )?;
- write!(
- output_file,
- "<p>\
- <a style='text-align: center;display: block;width: 100%;' \
- href='../error-index.html'>Back to list of error codes</a>\
- </p>",
- )?;
-
- self.footer(&mut output_file)
- }
-
- fn header(
- &self,
- output: &mut dyn Write,
- extra_path: &str,
- extra: &str,
- ) -> Result<(), Box<dyn Error>> {
- write!(
- output,
- r##"<!DOCTYPE html>
-<html>
-<head>
-<title>Rust Compiler Error Index</title>
-<meta charset="utf-8">
-<!-- Include rust.css after light.css so its rules take priority. -->
-<link rel="stylesheet" type="text/css" href="{extra_path}rustdoc{suffix}.css"/>
-<link rel="stylesheet" type="text/css" href="{extra_path}light{suffix}.css"/>
-<link rel="stylesheet" type="text/css" href="{extra_path}rust.css"/>
-<style>
-.error-undescribed {{
- display: none;
-}}
-</style>{extra}
-</head>
-<body>
-"##,
- suffix = self.0,
- )?;
- Ok(())
- }
-
- fn title(&self, output: &mut dyn Write, title: &str) -> Result<(), Box<dyn Error>> {
- write!(output, "<h1>{}</h1>\n", title)?;
- Ok(())
- }
-
- fn footer(&self, output: &mut dyn Write) -> Result<(), Box<dyn Error>> {
- write!(output, "</body></html>")?;
- Ok(())
- }
-}
-
/// Output an HTML page for the errors in `err_map` to `output_path`.
fn render_markdown(output_path: &Path) -> Result<(), Box<dyn Error>> {
let mut output_file = File::create(output_path)?;
Ok(())
}
-fn render_html(output_path: &Path, formatter: HTMLFormatter) -> Result<(), Box<dyn Error>> {
- let mut output_file = File::create(output_path)?;
+// By default, mdbook doesn't consider code blocks as Rust ones contrary to rustdoc so we have
+// to manually add `rust` attribute whenever needed.
+fn add_rust_attribute_on_codeblock(explanation: &str) -> String {
+ // Very hacky way to add the rust attribute on all code blocks.
+ let mut skip = true;
+ explanation.split("\n```").fold(String::new(), |mut acc, part| {
+ if !acc.is_empty() {
+ acc.push_str("\n```");
+ }
+ if !skip {
+ if let Some(attrs) = part.split('\n').next() {
+ if !attrs.contains("rust")
+ && (attrs.is_empty()
+ || attrs.contains("compile_fail")
+ || attrs.contains("ignore")
+ || attrs.contains("edition"))
+ {
+ if !attrs.is_empty() {
+ acc.push_str("rust,");
+ } else {
+ acc.push_str("rust");
+ }
+ }
+ }
+ }
+ skip = !skip;
+ acc.push_str(part);
+ acc
+ })
+}
- let error_codes_dir = "error_codes";
+fn render_html(output_path: &Path) -> Result<(), Box<dyn Error>> {
+ let mut introduction = format!(
+ "<script src='redirect.js'></script>
+# Rust error codes index
- let parent = output_path.parent().expect("There should have a parent").join(error_codes_dir);
+This page lists all the error codes emitted by the Rust compiler.
- if !parent.exists() {
- create_dir_all(&parent)?;
- }
+"
+ );
- formatter.header(
- &mut output_file,
- "",
- &format!(
- r#"<script>(function() {{
- if (window.location.hash) {{
- let code = window.location.hash.replace(/^#/, '');
- // We have to make sure this pattern matches to avoid inadvertently creating an
- // open redirect.
- if (/^E[0-9]+$/.test(code)) {{
- window.location = './{error_codes_dir}/' + code + '.html';
- }}
- }}
-}})()</script>"#
- ),
- )?;
- formatter.title(&mut output_file, "Rust Compiler Error Index")?;
+ let err_codes = error_codes();
+ let mut chapters = Vec::with_capacity(err_codes.len());
- write!(
- output_file,
- "<p>This page lists all the error codes emitted by the Rust compiler. If you want a full \
- explanation on an error code, click on it.</p>\
- <ul>",
- )?;
- for (err_code, explanation) in error_codes().iter() {
+ for (err_code, explanation) in err_codes.iter() {
if let Some(explanation) = explanation {
- write!(
- output_file,
- "<li><a href='./{0}/{1}.html'>{1}</a></li>",
- error_codes_dir, err_code
- )?;
- formatter.create_error_code_file(err_code, explanation, &parent)?;
+ introduction.push_str(&format!(" * [{0}](./{0}.html)\n", err_code));
+
+ let content = add_rust_attribute_on_codeblock(explanation);
+ chapters.push(BookItem::Chapter(Chapter {
+ name: err_code.to_string(),
+ content: format!("# Error code {}\n\n{}\n", err_code, content),
+ number: None,
+ sub_items: Vec::new(),
+ // We generate it into the `error_codes` folder.
+ path: Some(PathBuf::from(&format!("{}.html", err_code))),
+ source_path: None,
+ parent_names: Vec::new(),
+ }));
} else {
- write!(output_file, "<li>{}</li>", err_code)?;
+ introduction.push_str(&format!(" * {}\n", err_code));
}
}
- write!(output_file, "</ul>")?;
- formatter.footer(&mut output_file)
+
+ let mut config = Config::from_str(include_str!("book_config.toml"))?;
+ config.build.build_dir = output_path.join("error_codes").to_path_buf();
+ let mut book = MDBook::load_with_config_and_summary(
+ env!("CARGO_MANIFEST_DIR"),
+ config,
+ parse_summary("")?,
+ )?;
+ let chapter = Chapter {
+ name: "Rust error codes index".to_owned(),
+ content: introduction,
+ number: None,
+ sub_items: chapters,
+ // Very important: this file is named as `error-index.html` and not `index.html`!
+ path: Some(PathBuf::from("error-index.html")),
+ source_path: None,
+ parent_names: Vec::new(),
+ };
+ book.book.sections.push(BookItem::Chapter(chapter));
+ book.build()?;
+
+ // We can't put this content into another file, otherwise `mbdbook` will also put it into the
+ // output directory, making a duplicate.
+ fs::write(
+ output_path.join("error-index.html"),
+ r#"<!DOCTYPE html>
+<html>
+ <head>
+ <title>Rust error codes index - Error codes index</title>
+ <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
+ <meta name="description" content="Book listing all Rust error codes">
+ <script src="error_codes/redirect.js"></script>
+ </head>
+ <body>
+ <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>.
+ <script>document.getElementById("index-link").click()</script>
+ </body>
+</html>"#,
+ )?;
+
+ // No need for a 404 file, it's already handled by the server.
+ fs::remove_file(output_path.join("error_codes/404.html"))?;
+
+ Ok(())
}
fn main_with_result(format: OutputFormat, dst: &Path) -> Result<(), Box<dyn Error>> {
match format {
OutputFormat::Unknown(s) => panic!("Unknown output format: {}", s),
- OutputFormat::HTML(h) => render_html(dst, h),
+ OutputFormat::HTML => render_html(dst),
OutputFormat::Markdown => render_markdown(dst),
}
}
let mut args = env::args().skip(1);
let format = args.next();
let dst = args.next();
- let resource_suffix = args.next().unwrap_or_else(String::new);
- let format = format
- .map(|a| OutputFormat::from(&a, &resource_suffix))
- .unwrap_or(OutputFormat::from("html", &resource_suffix));
+ let format = format.map(|a| OutputFormat::from(&a)).unwrap_or(OutputFormat::from("html"));
let dst = dst.map(PathBuf::from).unwrap_or_else(|| match format {
- OutputFormat::HTML(..) => PathBuf::from("doc/error-index.html"),
+ OutputFormat::HTML => PathBuf::from("doc"),
OutputFormat::Markdown => PathBuf::from("doc/error-index.md"),
OutputFormat::Unknown(..) => PathBuf::from("<nul>"),
});
fn main() {
rustc_driver::init_env_logger("RUST_LOG");
let (format, dst) = parse_args();
- let result =
- rustc_span::create_default_session_globals_then(move || main_with_result(format, &dst));
+ let result = main_with_result(format, &dst);
if let Err(e) = result {
- panic!("{}", e.to_string());
+ panic!("{:?}", e);
}
}