blob: 2de2d959a95d745a3f656fa967804bd113042605 [file] [log] [blame]
#![feature(rustc_private)]
extern crate rustc_driver;
extern crate rustc_log;
extern crate rustc_session;
extern crate rustc_errors;
use rustc_errors::codes::DIAGNOSTICS;
use std::env;
use std::error::Error;
use std::fs::{self, File};
use std::io::Write;
use std::path::Path;
use std::path::PathBuf;
use std::str::FromStr;
use mdbook::book::{parse_summary, BookItem, Chapter};
use mdbook::{Config, MDBook};
enum OutputFormat {
HTML,
Markdown,
Unknown(String),
}
impl OutputFormat {
fn from(format: &str) -> OutputFormat {
match &*format.to_lowercase() {
"html" => OutputFormat::HTML,
"markdown" => OutputFormat::Markdown,
s => OutputFormat::Unknown(s.to_owned()),
}
}
}
/// 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)?;
write!(output_file, "# Rust Compiler Error Index\n")?;
for (err_code, description) in DIAGNOSTICS.iter() {
write!(output_file, "## {}\n{}\n", err_code, description)?
}
Ok(())
}
// 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
})
}
fn render_html(output_path: &Path) -> Result<(), Box<dyn Error>> {
let mut introduction = format!(
"# Rust error codes index
This page lists all the error codes emitted by the Rust compiler.
"
);
let err_codes = DIAGNOSTICS;
let mut chapters = Vec::with_capacity(err_codes.len());
for (err_code, explanation) in err_codes.iter() {
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(),
}));
}
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()?;
// The error-index used to be generated manually (without mdbook), and the
// index was located at the top level. Now that it is generated with
// mdbook, error-index.html has moved to error_codes/error-index.html.
// This adds a redirect so that old links go to the new location.
//
// We can't put this content into another file, otherwise `mdbook` 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>.
</body>
</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 => render_html(dst),
OutputFormat::Markdown => render_markdown(dst),
}
}
fn parse_args() -> (OutputFormat, PathBuf) {
let mut args = env::args().skip(1);
let format = args.next();
let dst = args.next();
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"),
OutputFormat::Markdown => PathBuf::from("doc/error-index.md"),
OutputFormat::Unknown(..) => PathBuf::from("<nul>"),
});
(format, dst)
}
fn main() {
let handler =
rustc_session::EarlyDiagCtxt::new(rustc_session::config::ErrorOutputType::default());
rustc_driver::init_logger(&handler, rustc_log::LoggerConfig::from_env("RUST_LOG"));
let (format, dst) = parse_args();
let result = main_with_result(format, &dst);
if let Err(e) = result {
panic!("{:?}", e);
}
}