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