]> git.proxmox.com Git - rustc.git/blobdiff - src/tools/rustbook/src/main.rs
New upstream version 1.43.0+dfsg1
[rustc.git] / src / tools / rustbook / src / main.rs
index fc283156693767ab0aca5de50a6d4b46fdd7a05a..01f324f0c5a0a653057e5c6690d39c274d7bb6a6 100644 (file)
@@ -8,11 +8,6 @@ use clap::{App, AppSettings, ArgMatches, SubCommand};
 use mdbook::errors::Result as Result3;
 use mdbook::MDBook;
 
-#[cfg(feature = "linkcheck")]
-use failure::Error;
-#[cfg(feature = "linkcheck")]
-use mdbook::renderer::RenderContext;
-
 fn main() {
     let d_message = "-d, --dest-dir=[dest-dir]
 'The output directory for your book{n}(Defaults to ./book when omitted)'";
@@ -30,6 +25,11 @@ fn main() {
                 .arg_from_usage(d_message)
                 .arg_from_usage(dir_message),
         )
+        .subcommand(
+            SubCommand::with_name("test")
+                .about("Tests that a book's Rust code samples compile")
+                .arg_from_usage(dir_message),
+        )
         .subcommand(
             SubCommand::with_name("linkcheck")
                 .about("Run linkcheck with mdBook 3")
@@ -41,20 +41,29 @@ fn main() {
     match matches.subcommand() {
         ("build", Some(sub_matches)) => {
             if let Err(e) = build(sub_matches) {
-                eprintln!("Error: {}", e);
-
-                for cause in e.iter().skip(1) {
-                    eprintln!("\tCaused By: {}", cause);
-                }
-
-                ::std::process::exit(101);
+                handle_error(e);
+            }
+        }
+        ("test", Some(sub_matches)) => {
+            if let Err(e) = test(sub_matches) {
+                handle_error(e);
             }
         }
         ("linkcheck", Some(sub_matches)) => {
             #[cfg(feature = "linkcheck")]
             {
-                if let Err(err) = linkcheck(sub_matches) {
-                    eprintln!("Error: {}", err);
+                let (diags, files) = linkcheck(sub_matches).expect("Error while linkchecking.");
+                if !diags.is_empty() {
+                    let color = codespan_reporting::term::termcolor::ColorChoice::Auto;
+                    let mut writer =
+                        codespan_reporting::term::termcolor::StandardStream::stderr(color);
+                    let cfg = codespan_reporting::term::Config::default();
+
+                    for diag in diags {
+                        codespan_reporting::term::emit(&mut writer, &cfg, &files, &diag)
+                            .expect("Unable to emit linkcheck error.");
+                    }
+
                     std::process::exit(101);
                 }
             }
@@ -73,14 +82,57 @@ fn main() {
 }
 
 #[cfg(feature = "linkcheck")]
-pub fn linkcheck(args: &ArgMatches<'_>) -> Result<(), Error> {
+pub fn linkcheck(
+    args: &ArgMatches<'_>,
+) -> Result<(Vec<codespan_reporting::diagnostic::Diagnostic>, codespan::Files), failure::Error> {
+    use mdbook_linkcheck::Reason;
+
     let book_dir = get_book_dir(args);
+    let src_dir = book_dir.join("src");
     let book = MDBook::load(&book_dir).unwrap();
-    let cfg = book.config;
-    let render_ctx = RenderContext::new(&book_dir, book.book, cfg, &book_dir);
-    let cache_file = render_ctx.destination.join("cache.json");
-    let color = codespan_reporting::term::termcolor::ColorChoice::Auto;
-    mdbook_linkcheck::run(&cache_file, color, &render_ctx)
+    let linkck_cfg = mdbook_linkcheck::get_config(&book.config)?;
+    let mut files = codespan::Files::new();
+    let target_files = mdbook_linkcheck::load_files_into_memory(&book.book, &mut files);
+    let cache = mdbook_linkcheck::Cache::default();
+
+    let (links, incomplete) = mdbook_linkcheck::extract_links(target_files, &files);
+
+    let outcome =
+        mdbook_linkcheck::validate(&links, &linkck_cfg, &src_dir, &cache, &files, incomplete)?;
+
+    let mut is_real_error = false;
+
+    for link in outcome.invalid_links.iter() {
+        match &link.reason {
+            Reason::FileNotFound | Reason::TraversesParentDirectories => {
+                is_real_error = true;
+            }
+            Reason::UnsuccessfulServerResponse(status) => {
+                if status.is_client_error() {
+                    is_real_error = true;
+                } else {
+                    eprintln!("Unsuccessful server response for link `{}`", link.link.uri);
+                }
+            }
+            Reason::Client(err) => {
+                if err.is_timeout() {
+                    eprintln!("Timeout for link `{}`", link.link.uri);
+                } else if err.is_server_error() {
+                    eprintln!("Server error for link `{}`", link.link.uri);
+                } else if !err.is_http() {
+                    eprintln!("Non-HTTP-related error for link: {} {}", link.link.uri, err);
+                } else {
+                    is_real_error = true;
+                }
+            }
+        }
+    }
+
+    if is_real_error {
+        Ok((outcome.generate_diagnostics(&files, linkck_cfg.warning_policy), files))
+    } else {
+        Ok((vec![], files))
+    }
 }
 
 // Build command implementation
@@ -100,6 +152,12 @@ pub fn build(args: &ArgMatches<'_>) -> Result3<()> {
     Ok(())
 }
 
+fn test(args: &ArgMatches<'_>) -> Result3<()> {
+    let book_dir = get_book_dir(args);
+    let mut book = MDBook::load(&book_dir)?;
+    book.test(vec![])
+}
+
 fn get_book_dir(args: &ArgMatches<'_>) -> PathBuf {
     if let Some(dir) = args.value_of("dir") {
         // Check if path is relative from current dir, or absolute...
@@ -109,3 +167,13 @@ fn get_book_dir(args: &ArgMatches<'_>) -> PathBuf {
         env::current_dir().unwrap()
     }
 }
+
+fn handle_error(error: mdbook::errors::Error) -> ! {
+    eprintln!("Error: {}", error);
+
+    for cause in error.iter().skip(1) {
+        eprintln!("\tCaused By: {}", cause);
+    }
+
+    ::std::process::exit(101);
+}