11use anyhow:: { Context as _, Error , Result } ;
2- use std:: { env, path:: Path } ;
2+ use std:: { env, fs :: File , io :: Write as _ , path:: Path } ;
33
44mod tracked {
55 use std:: {
@@ -68,13 +68,27 @@ mod tracked {
6868 }
6969}
7070
71+ type ETagMap < ' a > = phf_codegen:: Map < ' a , String > ;
72+
7173fn main ( ) -> Result < ( ) > {
7274 let out_dir = env:: var ( "OUT_DIR" ) . context ( "missing OUT_DIR" ) ?;
7375 let out_dir = Path :: new ( & out_dir) ;
7476 read_git_version ( ) ?;
75- compile_sass ( out_dir) ?;
77+
78+ let mut etag_map: ETagMap = ETagMap :: new ( ) ;
79+
80+ compile_sass ( out_dir, & mut etag_map) ?;
7681 write_known_targets ( out_dir) ?;
7782 compile_syntax ( out_dir) . context ( "could not compile syntax files" ) ?;
83+ calculate_static_etags ( & mut etag_map) ?;
84+
85+ let mut etag_file = File :: create ( out_dir. join ( "static_etag_map.rs" ) ) ?;
86+ writeln ! (
87+ & mut etag_file,
88+ "pub static STATIC_ETAG_MAP: ::phf::Map<&'static str, &'static str> = {};" ,
89+ etag_map. build( )
90+ ) ?;
91+ etag_file. sync_all ( ) ?;
7892
7993 // trigger recompilation when a new migration is added
8094 println ! ( "cargo:rerun-if-changed=migrations" ) ;
@@ -118,6 +132,16 @@ fn get_git_hash() -> Result<Option<String>> {
118132 }
119133}
120134
135+ fn etag_from_path ( path : impl AsRef < Path > ) -> Result < String > {
136+ Ok ( etag_from_content ( std:: fs:: read ( & path) ?) )
137+ }
138+
139+ fn etag_from_content ( content : impl AsRef < [ u8 ] > ) -> String {
140+ let digest = md5:: compute ( content) ;
141+ let md5_hex = format ! ( "{:x}" , digest) ;
142+ format ! ( r#""\"{md5_hex}\"""# )
143+ }
144+
121145fn compile_sass_file ( src : & Path , dest : & Path ) -> Result < ( ) > {
122146 let css = grass:: from_path (
123147 src. to_str ( )
@@ -133,7 +157,7 @@ fn compile_sass_file(src: &Path, dest: &Path) -> Result<()> {
133157 Ok ( ( ) )
134158}
135159
136- fn compile_sass ( out_dir : & Path ) -> Result < ( ) > {
160+ fn compile_sass ( out_dir : & Path , etag_map : & mut ETagMap ) -> Result < ( ) > {
137161 const STYLE_DIR : & str = "templates/style" ;
138162
139163 for entry in walkdir:: WalkDir :: new ( STYLE_DIR ) {
@@ -152,6 +176,9 @@ fn compile_sass(out_dir: &Path) -> Result<()> {
152176 compile_sass_file ( entry. path ( ) , & dest) . with_context ( || {
153177 format ! ( "compiling {} to {}" , entry. path( ) . display( ) , dest. display( ) )
154178 } ) ?;
179+
180+ let rel_str = dest. file_name ( ) . unwrap ( ) . to_string_lossy ( ) . to_string ( ) ;
181+ etag_map. entry ( rel_str, etag_from_path ( & dest) ?) ;
155182 }
156183 }
157184 }
@@ -160,7 +187,32 @@ fn compile_sass(out_dir: &Path) -> Result<()> {
160187 let pure = tracked:: read_to_string ( "vendor/pure-css/css/pure-min.css" ) ?;
161188 let grids = tracked:: read_to_string ( "vendor/pure-css/css/grids-responsive-min.css" ) ?;
162189 let vendored = pure + & grids;
163- std:: fs:: write ( out_dir. join ( "vendored" ) . with_extension ( "css" ) , vendored) ?;
190+ std:: fs:: write ( out_dir. join ( "vendored" ) . with_extension ( "css" ) , & vendored) ?;
191+
192+ etag_map. entry (
193+ "vendored.css" . to_owned ( ) ,
194+ etag_from_content ( vendored. as_bytes ( ) ) ,
195+ ) ;
196+
197+ Ok ( ( ) )
198+ }
199+
200+ fn calculate_static_etags ( etag_map : & mut ETagMap ) -> Result < ( ) > {
201+ const STATIC_DIRS : & [ & str ] = & [ "static" , "vendor" ] ;
202+
203+ for static_dir in STATIC_DIRS {
204+ for entry in walkdir:: WalkDir :: new ( static_dir) {
205+ let entry = entry?;
206+ let path = entry. path ( ) ;
207+ if !path. is_file ( ) {
208+ continue ;
209+ }
210+
211+ let rel = path. strip_prefix ( static_dir) . unwrap ( ) ;
212+ let rel_str = rel. to_string_lossy ( ) . replace ( '\\' , "/" ) ;
213+ etag_map. entry ( rel_str, etag_from_path ( path) ?) ;
214+ }
215+ }
164216
165217 Ok ( ( ) )
166218}
0 commit comments