1
+ use crate :: utils:: { ErrAction , expect_action} ;
2
+ use core:: fmt:: Display ;
3
+ use core:: mem;
1
4
use std:: path:: Path ;
2
5
use std:: process:: Command ;
3
6
use std:: time:: { Duration , SystemTime } ;
4
- use std:: { env, thread} ;
7
+ use std:: { env, fs, thread} ;
8
+ use walkdir:: WalkDir ;
5
9
6
10
#[ cfg( windows) ]
7
11
const PYTHON : & str = "python" ;
@@ -18,56 +22,85 @@ pub fn run(port: u16, lint: Option<String>) -> ! {
18
22
Some ( lint) => format ! ( "http://localhost:{port}/#{lint}" ) ,
19
23
} ) ;
20
24
25
+ let mut last_update = mtime ( "util/gh-pages/index.html" ) ;
21
26
loop {
22
- let index_time = mtime ( "util/gh-pages/index.html" ) ;
23
- let times = [
24
- "clippy_lints/src" ,
25
- "util/gh-pages/index_template.html" ,
26
- "tests/compile-test.rs" ,
27
- ]
28
- . map ( mtime) ;
29
-
30
- if times. iter ( ) . any ( |& time| index_time < time) {
31
- Command :: new ( env:: var ( "CARGO" ) . unwrap_or_else ( |_| "cargo" . into ( ) ) )
32
- . arg ( "collect-metadata" )
33
- . spawn ( )
34
- . unwrap ( )
35
- . wait ( )
36
- . unwrap ( ) ;
27
+ if is_metadata_outdated ( mem:: replace ( & mut last_update, SystemTime :: now ( ) ) ) {
28
+ // Ignore the command result; we'll fall back to displaying the old metadata.
29
+ let _ = expect_action (
30
+ Command :: new ( env:: var ( "CARGO" ) . unwrap_or_else ( |_| "cargo" . into ( ) ) )
31
+ . arg ( "collect-metadata" )
32
+ . status ( ) ,
33
+ ErrAction :: Run ,
34
+ "cargo collect-metadata" ,
35
+ ) ;
36
+ last_update = SystemTime :: now ( ) ;
37
37
}
38
+
39
+ // Only start the web server the first time around.
38
40
if let Some ( url) = url. take ( ) {
39
41
thread:: spawn ( move || {
40
- let mut child = Command :: new ( PYTHON )
41
- . arg ( "-m" )
42
- . arg ( "http.server" )
43
- . arg ( port. to_string ( ) )
44
- . current_dir ( "util/gh-pages" )
45
- . spawn ( )
46
- . unwrap ( ) ;
42
+ let mut child = expect_action (
43
+ Command :: new ( PYTHON )
44
+ . args ( [ "-m" , "http.server" , port. to_string ( ) . as_str ( ) ] )
45
+ . current_dir ( "util/gh-pages" )
46
+ . spawn ( ) ,
47
+ ErrAction :: Run ,
48
+ "python -m http.server" ,
49
+ ) ;
47
50
// Give some time for python to start
48
51
thread:: sleep ( Duration :: from_millis ( 500 ) ) ;
49
52
// Launch browser after first export.py has completed and http.server is up
50
53
let _result = opener:: open ( url) ;
51
- child. wait ( ) . unwrap ( ) ;
54
+ expect_action ( child. wait ( ) , ErrAction :: Run , "python -m http.server" ) ;
52
55
} ) ;
53
56
}
57
+
58
+ // Delay to avoid updating the metadata too aggressively.
54
59
thread:: sleep ( Duration :: from_millis ( 1000 ) ) ;
55
60
}
56
61
}
57
62
58
- fn mtime ( path : impl AsRef < Path > ) -> SystemTime {
59
- let path = path. as_ref ( ) ;
60
- if path. is_dir ( ) {
61
- path. read_dir ( )
62
- . into_iter ( )
63
- . flatten ( )
64
- . flatten ( )
65
- . map ( |entry| mtime ( entry. path ( ) ) )
66
- . max ( )
67
- . unwrap_or ( SystemTime :: UNIX_EPOCH )
68
- } else {
69
- path. metadata ( )
70
- . and_then ( |metadata| metadata. modified ( ) )
71
- . unwrap_or ( SystemTime :: UNIX_EPOCH )
63
+ fn log_err_and_continue < T > ( res : Result < T , impl Display > , path : & Path ) -> Option < T > {
64
+ match res {
65
+ Ok ( x) => Some ( x) ,
66
+ Err ( ref e) => {
67
+ eprintln ! ( "error reading `{}`: {e}" , path. display( ) ) ;
68
+ None
69
+ } ,
72
70
}
73
71
}
72
+
73
+ fn mtime ( path : & str ) -> SystemTime {
74
+ log_err_and_continue ( fs:: metadata ( path) , path. as_ref ( ) )
75
+ . and_then ( |metadata| log_err_and_continue ( metadata. modified ( ) , path. as_ref ( ) ) )
76
+ . unwrap_or ( SystemTime :: UNIX_EPOCH )
77
+ }
78
+
79
+ fn is_metadata_outdated ( time : SystemTime ) -> bool {
80
+ // Ignore all IO errors here. We don't want to stop them from hosting the server.
81
+ if time < mtime ( "util/gh-pages/index_template.html" ) || time < mtime ( "tests/compile-test.rs" ) {
82
+ return true ;
83
+ }
84
+ let Some ( dir) = log_err_and_continue ( fs:: read_dir ( "." ) , "." . as_ref ( ) ) else {
85
+ return false ;
86
+ } ;
87
+ dir. map_while ( |e| log_err_and_continue ( e, "." . as_ref ( ) ) ) . any ( |e| {
88
+ let name = e. file_name ( ) ;
89
+ let name_bytes = name. as_encoded_bytes ( ) ;
90
+ if ( name_bytes. starts_with ( b"clippy_lints" ) && name_bytes != b"clippy_lints_internal" )
91
+ || name_bytes == b"clippy_config"
92
+ {
93
+ WalkDir :: new ( & name)
94
+ . into_iter ( )
95
+ . map_while ( |e| log_err_and_continue ( e, name. as_ref ( ) ) )
96
+ . filter ( |e| e. file_type ( ) . is_file ( ) )
97
+ . filter_map ( |e| {
98
+ log_err_and_continue ( e. metadata ( ) , e. path ( ) )
99
+ . and_then ( |m| log_err_and_continue ( m. modified ( ) , e. path ( ) ) )
100
+ } )
101
+ . any ( |ftime| time < ftime)
102
+ } else {
103
+ false
104
+ }
105
+ } )
106
+ }
0 commit comments