17
17
//! should catch the majority of "broken link" cases.
18
18
19
19
use std:: cell:: { Cell , RefCell } ;
20
+ use std:: collections:: hash_map:: Entry ;
20
21
use std:: collections:: { HashMap , HashSet } ;
21
22
use std:: fs;
22
23
use std:: io:: ErrorKind ;
24
+ use std:: iter:: once;
23
25
use std:: path:: { Component , Path , PathBuf } ;
24
26
use std:: rc:: Rc ;
25
27
use std:: time:: Instant ;
@@ -112,6 +114,7 @@ macro_rules! t {
112
114
113
115
struct Cli {
114
116
docs : PathBuf ,
117
+ link_targets_dirs : Vec < PathBuf > ,
115
118
}
116
119
117
120
fn main ( ) {
@@ -123,7 +126,11 @@ fn main() {
123
126
}
124
127
} ;
125
128
126
- let mut checker = Checker { root : cli. docs . clone ( ) , cache : HashMap :: new ( ) } ;
129
+ let mut checker = Checker {
130
+ root : cli. docs . clone ( ) ,
131
+ link_targets_dirs : cli. link_targets_dirs ,
132
+ cache : HashMap :: new ( ) ,
133
+ } ;
127
134
let mut report = Report {
128
135
errors : 0 ,
129
136
start : Instant :: now ( ) ,
@@ -150,13 +157,20 @@ fn parse_cli() -> Result<Cli, String> {
150
157
151
158
let mut verbatim = false ;
152
159
let mut docs = None ;
160
+ let mut link_targets_dirs = Vec :: new ( ) ;
153
161
154
162
let mut args = std:: env:: args ( ) . skip ( 1 ) ;
155
163
while let Some ( arg) = args. next ( ) {
156
164
if !verbatim && arg == "--" {
157
165
verbatim = true ;
158
166
} else if !verbatim && ( arg == "-h" || arg == "--help" ) {
159
167
usage_and_exit ( 0 )
168
+ } else if !verbatim && arg == "--link-targets-dir" {
169
+ link_targets_dirs. push ( to_canonical_path (
170
+ & args. next ( ) . ok_or ( "missing value for --link-targets-dir" ) ?,
171
+ ) ?) ;
172
+ } else if !verbatim && let Some ( value) = arg. strip_prefix ( "--link-targets-dir=" ) {
173
+ link_targets_dirs. push ( to_canonical_path ( value) ?) ;
160
174
} else if !verbatim && arg. starts_with ( '-' ) {
161
175
return Err ( format ! ( "unknown flag: {arg}" ) ) ;
162
176
} else if docs. is_none ( ) {
@@ -166,16 +180,20 @@ fn parse_cli() -> Result<Cli, String> {
166
180
}
167
181
}
168
182
169
- Ok ( Cli { docs : to_canonical_path ( & docs. ok_or ( "missing first positional argument" ) ?) ? } )
183
+ Ok ( Cli {
184
+ docs : to_canonical_path ( & docs. ok_or ( "missing first positional argument" ) ?) ?,
185
+ link_targets_dirs,
186
+ } )
170
187
}
171
188
172
189
fn usage_and_exit ( code : i32 ) -> ! {
173
- eprintln ! ( "usage: linkchecker <path> " ) ;
190
+ eprintln ! ( "usage: linkchecker PATH [--link-targets-dir=PATH ...] " ) ;
174
191
std:: process:: exit ( code)
175
192
}
176
193
177
194
struct Checker {
178
195
root : PathBuf ,
196
+ link_targets_dirs : Vec < PathBuf > ,
179
197
cache : Cache ,
180
198
}
181
199
@@ -468,15 +486,23 @@ impl Checker {
468
486
let pretty_path =
469
487
file. strip_prefix ( & self . root ) . unwrap_or ( file) . to_str ( ) . unwrap ( ) . to_string ( ) ;
470
488
471
- let entry =
472
- self . cache . entry ( pretty_path. clone ( ) ) . or_insert_with ( || match fs:: metadata ( file) {
489
+ for base in once ( & self . root ) . chain ( self . link_targets_dirs . iter ( ) ) {
490
+ let entry = self . cache . entry ( pretty_path. clone ( ) ) ;
491
+ if let Entry :: Occupied ( e) = & entry
492
+ && !matches ! ( e. get( ) , FileEntry :: Missing )
493
+ {
494
+ break ;
495
+ }
496
+
497
+ let file = base. join ( & pretty_path) ;
498
+ entry. insert_entry ( match fs:: metadata ( & file) {
473
499
Ok ( metadata) if metadata. is_dir ( ) => FileEntry :: Dir ,
474
500
Ok ( _) => {
475
501
if file. extension ( ) . and_then ( |s| s. to_str ( ) ) != Some ( "html" ) {
476
502
FileEntry :: OtherFile
477
503
} else {
478
504
report. html_files += 1 ;
479
- load_html_file ( file, report)
505
+ load_html_file ( & file, report)
480
506
}
481
507
}
482
508
Err ( e) if e. kind ( ) == ErrorKind :: NotFound => FileEntry :: Missing ,
@@ -492,6 +518,9 @@ impl Checker {
492
518
panic ! ( "unexpected read error for {}: {}" , file. display( ) , e) ;
493
519
}
494
520
} ) ;
521
+ }
522
+
523
+ let entry = self . cache . get ( & pretty_path) . unwrap ( ) ;
495
524
( pretty_path, entry)
496
525
}
497
526
}
0 commit comments