Skip to content

Commit 4ce157d

Browse files
authored
Rollup merge of #68791 - mark-i-m:proper-linkcheck, r=ehuss,JohnTitor
implement proper linkchecker hardening r? @JohnTitor This implements proper linkcheck filtering... we might need to fiddle with a bit to adjust what is or isn't filtered, but this seems to work reasonable locally.
2 parents 2d8f638 + 5e086c8 commit 4ce157d

File tree

4 files changed

+63
-15
lines changed

4 files changed

+63
-15
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3067,6 +3067,7 @@ name = "rustbook"
30673067
version = "0.1.0"
30683068
dependencies = [
30693069
"clap",
3070+
"codespan",
30703071
"codespan-reporting",
30713072
"failure",
30723073
"mdbook",

src/doc/rustc-guide

src/tools/rustbook/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@ license = "MIT OR Apache-2.0"
66
edition = "2018"
77

88
[features]
9-
linkcheck = ["mdbook-linkcheck", "codespan-reporting"]
9+
linkcheck = ["mdbook-linkcheck", "codespan-reporting", "codespan"]
1010

1111
[dependencies]
1212
clap = "2.25.0"
1313
failure = "0.1"
1414
mdbook-linkcheck = { version = "0.5.0", optional = true }
1515
# Keep in sync with mdbook-linkcheck.
16+
codespan = { version = "0.5", optional = true }
1617
codespan-reporting = { version = "0.5", optional = true }
1718

1819

src/tools/rustbook/src/main.rs

Lines changed: 59 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,6 @@ use clap::{App, AppSettings, ArgMatches, SubCommand};
88
use mdbook::errors::Result as Result3;
99
use mdbook::MDBook;
1010

11-
#[cfg(feature = "linkcheck")]
12-
use failure::Error;
13-
#[cfg(feature = "linkcheck")]
14-
use mdbook::renderer::RenderContext;
15-
1611
fn main() {
1712
let d_message = "-d, --dest-dir=[dest-dir]
1813
'The output directory for your book{n}(Defaults to ./book when omitted)'";
@@ -53,8 +48,18 @@ fn main() {
5348
("linkcheck", Some(sub_matches)) => {
5449
#[cfg(feature = "linkcheck")]
5550
{
56-
if let Err(err) = linkcheck(sub_matches) {
57-
eprintln!("Error: {}", err);
51+
let (diags, files) = linkcheck(sub_matches).expect("Error while linkchecking.");
52+
if !diags.is_empty() {
53+
let color = codespan_reporting::term::termcolor::ColorChoice::Auto;
54+
let mut writer =
55+
codespan_reporting::term::termcolor::StandardStream::stderr(color);
56+
let cfg = codespan_reporting::term::Config::default();
57+
58+
for diag in diags {
59+
codespan_reporting::term::emit(&mut writer, &cfg, &files, &diag)
60+
.expect("Unable to emit linkcheck error.");
61+
}
62+
5863
std::process::exit(101);
5964
}
6065
}
@@ -73,14 +78,55 @@ fn main() {
7378
}
7479

7580
#[cfg(feature = "linkcheck")]
76-
pub fn linkcheck(args: &ArgMatches<'_>) -> Result<(), Error> {
81+
pub fn linkcheck(
82+
args: &ArgMatches<'_>,
83+
) -> Result<(Vec<codespan_reporting::diagnostic::Diagnostic>, codespan::Files), failure::Error> {
84+
use mdbook_linkcheck::Reason;
85+
7786
let book_dir = get_book_dir(args);
87+
let src_dir = book_dir.join("src");
7888
let book = MDBook::load(&book_dir).unwrap();
79-
let cfg = book.config;
80-
let render_ctx = RenderContext::new(&book_dir, book.book, cfg, &book_dir);
81-
let cache_file = render_ctx.destination.join("cache.json");
82-
let color = codespan_reporting::term::termcolor::ColorChoice::Auto;
83-
mdbook_linkcheck::run(&cache_file, color, &render_ctx)
89+
let linkck_cfg = mdbook_linkcheck::get_config(&book.config)?;
90+
let mut files = codespan::Files::new();
91+
let target_files = mdbook_linkcheck::load_files_into_memory(&book.book, &mut files);
92+
let cache = mdbook_linkcheck::Cache::default();
93+
94+
let (links, incomplete) = mdbook_linkcheck::extract_links(target_files, &files);
95+
96+
let outcome =
97+
mdbook_linkcheck::validate(&links, &linkck_cfg, &src_dir, &cache, &files, incomplete)?;
98+
99+
let mut is_real_error = false;
100+
101+
for link in outcome.invalid_links.iter() {
102+
match &link.reason {
103+
Reason::FileNotFound | Reason::TraversesParentDirectories => {
104+
is_real_error = true;
105+
}
106+
Reason::UnsuccessfulServerResponse(status) => {
107+
if status.is_client_error() {
108+
is_real_error = true;
109+
} else {
110+
eprintln!("Unsuccessful server response for link `{}`", link.link.uri);
111+
}
112+
}
113+
Reason::Client(err) => {
114+
if err.is_timeout() {
115+
eprintln!("Timeout for link `{}`", link.link.uri);
116+
} else if err.is_server_error() {
117+
eprintln!("Server error for link `{}`", link.link.uri);
118+
} else {
119+
is_real_error = true;
120+
}
121+
}
122+
}
123+
}
124+
125+
if is_real_error {
126+
Ok((outcome.generate_diagnostics(&files, linkck_cfg.warning_policy), files))
127+
} else {
128+
Ok((vec![], files))
129+
}
84130
}
85131

86132
// Build command implementation

0 commit comments

Comments
 (0)