1+ use crate :: utils:: { ErrAction , cargo_cmd, expect_action} ;
2+ use core:: fmt:: Display ;
3+ use core:: mem;
14use std:: path:: Path ;
25use std:: process:: Command ;
36use std:: time:: { Duration , SystemTime } ;
4- use std:: { env, thread} ;
7+ use std:: { fs, thread} ;
8+ use walkdir:: WalkDir ;
59
610#[ cfg( windows) ]
711const PYTHON : & str = "python" ;
@@ -18,56 +22,83 @@ pub fn run(port: u16, lint: Option<String>) -> ! {
1822 Some ( lint) => format ! ( "http://localhost:{port}/#{lint}" ) ,
1923 } ) ;
2024
25+ let mut last_update = mtime ( "util/gh-pages/index.html" ) ;
2126 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 ( ) ;
3735 }
36+
37+ // Only start the web server the first time around.
3838 if let Some ( url) = url. take ( ) {
3939 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+ ) ;
4748 // Give some time for python to start
4849 thread:: sleep ( Duration :: from_millis ( 500 ) ) ;
4950 // Launch browser after first export.py has completed and http.server is up
5051 let _result = opener:: open ( url) ;
51- child. wait ( ) . unwrap ( ) ;
52+ expect_action ( child. wait ( ) , ErrAction :: Run , "python -m http.server" ) ;
5253 } ) ;
5354 }
55+
56+ // Delay to avoid updating the metadata too aggressively.
5457 thread:: sleep ( Duration :: from_millis ( 1000 ) ) ;
5558 }
5659}
5760
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+ } ,
7268 }
7369}
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