2222// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2323
2424use std:: cell:: Cell ;
25- use std:: collections:: HashMap ;
26- use std:: fs:: read_dir;
2725use std:: future:: IntoFuture ;
2826use std:: net:: { IpAddr , SocketAddr , TcpListener } ;
2927use std:: path:: { Path , PathBuf , MAIN_SEPARATOR } ;
@@ -41,7 +39,6 @@ use mime_guess::from_path as mimetype_from_path;
4139use time:: macros:: format_description;
4240use time:: { OffsetDateTime , UtcOffset } ;
4341
44- use libs:: globset:: GlobSet ;
4542use libs:: percent_encoding;
4643use libs:: relative_path:: { RelativePath , RelativePathBuf } ;
4744use libs:: serde_json;
@@ -52,23 +49,12 @@ use errors::{anyhow, Context, Error, Result};
5249use pathdiff:: diff_paths;
5350use site:: sass:: compile_sass;
5451use site:: { Site , SITE_CONTENT } ;
55- use utils:: fs:: { clean_site_output_folder, copy_file, is_temp_file } ;
52+ use utils:: fs:: { clean_site_output_folder, copy_file} ;
5653
57- use crate :: fs_event_utils :: { get_relevant_event_kind , SimpleFSEventKind } ;
54+ use crate :: fs_utils :: { filter_events , ChangeKind , SimpleFileSystemEventKind } ;
5855use crate :: messages;
5956use std:: ffi:: OsStr ;
6057
61- #[ derive( Clone , Copy , Debug , Hash , PartialEq ) ]
62- enum ChangeKind {
63- Content ,
64- Templates ,
65- Themes ,
66- StaticFiles ,
67- Sass ,
68- Config ,
69- }
70- impl Eq for ChangeKind { }
71-
7258#[ derive( Debug , PartialEq ) ]
7359enum WatchMode {
7460 Required ,
@@ -206,7 +192,7 @@ async fn response_error_injector(
206192 . map ( |req| {
207193 req. headers ( )
208194 . get ( header:: CONTENT_TYPE )
209- . map ( |val| val != & HeaderValue :: from_static ( "text/html" ) )
195+ . map ( |val| val != HeaderValue :: from_static ( "text/html" ) )
210196 . unwrap_or ( true )
211197 } )
212198 . unwrap_or ( true )
@@ -401,7 +387,7 @@ fn create_new_site(
401387
402388 let mut constructed_base_url = construct_url ( & base_url, no_port_append, interface_port) ;
403389
404- if !site. config . base_url . ends_with ( "/" ) && constructed_base_url != "/" {
390+ if !site. config . base_url . ends_with ( '/' ) && constructed_base_url != "/" {
405391 constructed_base_url. truncate ( constructed_base_url. len ( ) - 1 ) ;
406392 }
407393
@@ -548,7 +534,7 @@ pub fn serve(
548534 & constructed_base_url, & bind_address
549535 ) ;
550536 if open {
551- if let Err ( err) = open:: that ( format ! ( "{}" , & constructed_base_url) ) {
537+ if let Err ( err) = open:: that ( & constructed_base_url) {
552538 eprintln ! ( "Failed to open URL in your browser: {}" , err) ;
553539 }
554540 }
@@ -693,66 +679,16 @@ pub fn serve(
693679
694680 loop {
695681 match rx. recv ( ) {
696- Ok ( Ok ( mut events) ) => {
697- // Arrange events from oldest to newest.
698- events. sort_by ( |e1, e2| e1. time . cmp ( & e2. time ) ) ;
699-
700- // Use a map to keep only the last event that occurred for a particular path.
701- // Map `full_path -> (partial_path, simple_event_kind, change_kind)`.
702- let mut meaningful_events: HashMap <
703- PathBuf ,
704- ( PathBuf , SimpleFSEventKind , ChangeKind ) ,
705- > = HashMap :: new ( ) ;
706-
707- for event in events. iter ( ) {
708- let simple_kind = get_relevant_event_kind ( & event. event . kind ) ;
709- if simple_kind. is_none ( ) {
710- continue ;
711- }
712-
713- // We currently only handle notify events that report a single path per event.
714- if event. event . paths . len ( ) != 1 {
715- console:: error ( & format ! (
716- "Skipping unsupported file system event with multiple paths: {:?}" ,
717- event. event. kind
718- ) ) ;
719- continue ;
720- }
721- let path = event. event . paths [ 0 ] . clone ( ) ;
722-
723- if is_ignored_file ( & site. config . ignored_content_globset , & path) {
724- continue ;
725- }
726-
727- if is_temp_file ( & path) {
728- continue ;
729- }
730-
731- // We only care about changes in non-empty folders
732- if path. is_dir ( ) && is_folder_empty ( & path) {
733- continue ;
734- }
735-
736- let ( change_k, partial_p) = detect_change_kind ( & root_dir, & path, & config_path) ;
737- meaningful_events. insert ( path, ( partial_p, simple_kind. unwrap ( ) , change_k) ) ;
738- }
739-
740- if meaningful_events. is_empty ( ) {
682+ Ok ( Ok ( events) ) => {
683+ let changes = filter_events (
684+ events,
685+ root_dir,
686+ & config_path,
687+ & site. config . ignored_content_globset ,
688+ ) ;
689+ if changes. is_empty ( ) {
741690 continue ;
742691 }
743-
744- // Bin changes by change kind to support later iteration over batches of changes.
745- // Map of change_kind -> (partial_path, full_path, event_kind).
746- let mut changes: HashMap <
747- ChangeKind ,
748- Vec < ( & PathBuf , & PathBuf , & SimpleFSEventKind ) > ,
749- > = HashMap :: new ( ) ;
750- for ( full_path, ( partial_path, event_kind, change_kind) ) in meaningful_events. iter ( )
751- {
752- let c = changes. entry ( * change_kind) . or_insert ( vec ! [ ] ) ;
753- c. push ( ( partial_path, full_path, event_kind) ) ;
754- }
755-
756692 let format = format_description ! ( "[year]-[month]-[day] [hour]:[minute]:[second]" ) ;
757693
758694 for ( change_kind, change_group) in changes. iter ( ) {
@@ -774,7 +710,8 @@ pub fn serve(
774710 full_path. display( )
775711 ) ) ;
776712
777- let can_do_fast_reload = * * event_kind != SimpleFSEventKind :: Remove ;
713+ let can_do_fast_reload =
714+ * event_kind != SimpleFileSystemEventKind :: Remove ;
778715
779716 if fast_rebuild {
780717 if can_do_fast_reload {
@@ -783,9 +720,9 @@ pub fn serve(
783720 . unwrap_or_else ( || OsStr :: new ( "" ) )
784721 . to_string_lossy ( ) ;
785722 let res = if filename == "_index.md" {
786- site. add_and_render_section ( & full_path)
723+ site. add_and_render_section ( full_path)
787724 } else if filename. ends_with ( ".md" ) {
788- site. add_and_render_page ( & full_path)
725+ site. add_and_render_page ( full_path)
789726 } else {
790727 // an asset changed? a folder renamed?
791728 // should we make it smarter so it doesn't reload the whole site?
@@ -816,9 +753,9 @@ pub fn serve(
816753 }
817754 ChangeKind :: Templates => {
818755 let partial_paths: Vec < & PathBuf > =
819- change_group. iter ( ) . map ( |( p, _, _) | * p) . collect ( ) ;
756+ change_group. iter ( ) . map ( |( p, _, _) | p) . collect ( ) ;
820757 let full_paths: Vec < & PathBuf > =
821- change_group. iter ( ) . map ( |( _, p, _) | * p) . collect ( ) ;
758+ change_group. iter ( ) . map ( |( _, p, _) | p) . collect ( ) ;
822759 let combined_paths = full_paths
823760 . iter ( )
824761 . map ( |p| p. display ( ) . to_string ( ) )
@@ -842,11 +779,11 @@ pub fn serve(
842779 }
843780 ChangeKind :: StaticFiles => {
844781 for ( partial_path, full_path, _) in change_group. iter ( ) {
845- copy_static ( & site, & full_path, & partial_path) ;
782+ copy_static ( & site, full_path, partial_path) ;
846783 }
847784 }
848785 ChangeKind :: Sass => {
849- let full_paths = change_group. iter ( ) . map ( |( _, p, _) | * p) . collect ( ) ;
786+ let full_paths = change_group. iter ( ) . map ( |( _, p, _) | p) . collect ( ) ;
850787 reload_sass ( & site, & full_paths) ;
851788 }
852789 ChangeKind :: Themes => {
@@ -875,141 +812,9 @@ pub fn serve(
875812 }
876813}
877814
878- fn is_ignored_file ( ignored_content_globset : & Option < GlobSet > , path : & Path ) -> bool {
879- match ignored_content_globset {
880- Some ( gs) => gs. is_match ( path) ,
881- None => false ,
882- }
883- }
884-
885- /// Detect what changed from the given path so we have an idea what needs
886- /// to be reloaded
887- fn detect_change_kind ( pwd : & Path , path : & Path , config_path : & Path ) -> ( ChangeKind , PathBuf ) {
888- let mut partial_path = PathBuf :: from ( "/" ) ;
889- partial_path. push ( path. strip_prefix ( pwd) . unwrap_or ( path) ) ;
890-
891- let change_kind = if partial_path. starts_with ( "/templates" ) {
892- ChangeKind :: Templates
893- } else if partial_path. starts_with ( "/themes" ) {
894- ChangeKind :: Themes
895- } else if partial_path. starts_with ( "/content" ) {
896- ChangeKind :: Content
897- } else if partial_path. starts_with ( "/static" ) {
898- ChangeKind :: StaticFiles
899- } else if partial_path. starts_with ( "/sass" ) {
900- ChangeKind :: Sass
901- } else if path == config_path {
902- ChangeKind :: Config
903- } else {
904- unreachable ! ( "Got a change in an unexpected path: {}" , partial_path. display( ) ) ;
905- } ;
906-
907- ( change_kind, partial_path)
908- }
909-
910- /// Check if the directory at path contains any file
911- fn is_folder_empty ( dir : & Path ) -> bool {
912- // Can panic if we don't have the rights I guess?
913-
914- read_dir ( dir) . expect ( "Failed to read a directory to see if it was empty" ) . next ( ) . is_none ( )
915- }
916-
917815#[ cfg( test) ]
918816mod tests {
919- use std:: path:: { Path , PathBuf } ;
920-
921- use super :: { construct_url, detect_change_kind, is_temp_file, ChangeKind } ;
922-
923- #[ test]
924- fn can_recognize_temp_files ( ) {
925- let test_cases = vec ! [
926- Path :: new( "hello.swp" ) ,
927- Path :: new( "hello.swx" ) ,
928- Path :: new( ".DS_STORE" ) ,
929- Path :: new( "hello.tmp" ) ,
930- Path :: new( "hello.html.__jb_old___" ) ,
931- Path :: new( "hello.html.__jb_tmp___" ) ,
932- Path :: new( "hello.html.__jb_bak___" ) ,
933- Path :: new( "hello.html~" ) ,
934- Path :: new( "#hello.html" ) ,
935- Path :: new( ".index.md.kate-swp" ) ,
936- ] ;
937-
938- for t in test_cases {
939- assert ! ( is_temp_file( t) ) ;
940- }
941- }
942-
943- #[ test]
944- fn can_detect_kind_of_changes ( ) {
945- let test_cases = vec ! [
946- (
947- ( ChangeKind :: Templates , PathBuf :: from( "/templates/hello.html" ) ) ,
948- Path :: new( "/home/vincent/site" ) ,
949- Path :: new( "/home/vincent/site/templates/hello.html" ) ,
950- Path :: new( "/home/vincent/site/config.toml" ) ,
951- ) ,
952- (
953- ( ChangeKind :: Themes , PathBuf :: from( "/themes/hello.html" ) ) ,
954- Path :: new( "/home/vincent/site" ) ,
955- Path :: new( "/home/vincent/site/themes/hello.html" ) ,
956- Path :: new( "/home/vincent/site/config.toml" ) ,
957- ) ,
958- (
959- ( ChangeKind :: StaticFiles , PathBuf :: from( "/static/site.css" ) ) ,
960- Path :: new( "/home/vincent/site" ) ,
961- Path :: new( "/home/vincent/site/static/site.css" ) ,
962- Path :: new( "/home/vincent/site/config.toml" ) ,
963- ) ,
964- (
965- ( ChangeKind :: Content , PathBuf :: from( "/content/posts/hello.md" ) ) ,
966- Path :: new( "/home/vincent/site" ) ,
967- Path :: new( "/home/vincent/site/content/posts/hello.md" ) ,
968- Path :: new( "/home/vincent/site/config.toml" ) ,
969- ) ,
970- (
971- ( ChangeKind :: Sass , PathBuf :: from( "/sass/print.scss" ) ) ,
972- Path :: new( "/home/vincent/site" ) ,
973- Path :: new( "/home/vincent/site/sass/print.scss" ) ,
974- Path :: new( "/home/vincent/site/config.toml" ) ,
975- ) ,
976- (
977- ( ChangeKind :: Config , PathBuf :: from( "/config.toml" ) ) ,
978- Path :: new( "/home/vincent/site" ) ,
979- Path :: new( "/home/vincent/site/config.toml" ) ,
980- Path :: new( "/home/vincent/site/config.toml" ) ,
981- ) ,
982- (
983- ( ChangeKind :: Config , PathBuf :: from( "/config.staging.toml" ) ) ,
984- Path :: new( "/home/vincent/site" ) ,
985- Path :: new( "/home/vincent/site/config.staging.toml" ) ,
986- Path :: new( "/home/vincent/site/config.staging.toml" ) ,
987- ) ,
988- ] ;
989-
990- for ( expected, pwd, path, config_filename) in test_cases {
991- assert_eq ! ( expected, detect_change_kind( pwd, path, config_filename) ) ;
992- }
993- }
994-
995- #[ test]
996- #[ cfg( windows) ]
997- fn windows_path_handling ( ) {
998- let expected = ( ChangeKind :: Templates , PathBuf :: from ( "/templates/hello.html" ) ) ;
999- let pwd = Path :: new ( r#"C:\Users\johan\site"# ) ;
1000- let path = Path :: new ( r#"C:\Users\johan\site\templates\hello.html"# ) ;
1001- let config_filename = Path :: new ( r#"C:\Users\johan\site\config.toml"# ) ;
1002- assert_eq ! ( expected, detect_change_kind( pwd, path, config_filename) ) ;
1003- }
1004-
1005- #[ test]
1006- fn relative_path ( ) {
1007- let expected = ( ChangeKind :: Templates , PathBuf :: from ( "/templates/hello.html" ) ) ;
1008- let pwd = Path :: new ( "/home/johan/site" ) ;
1009- let path = Path :: new ( "templates/hello.html" ) ;
1010- let config_filename = Path :: new ( "config.toml" ) ;
1011- assert_eq ! ( expected, detect_change_kind( pwd, path, config_filename) ) ;
1012- }
817+ use super :: construct_url;
1013818
1014819 #[ test]
1015820 fn test_construct_url_base_url_is_slash ( ) {
0 commit comments