@@ -658,11 +658,11 @@ impl ToTokens for OptionBytesSlice {
658658 }
659659}
660660
661- struct EmbeddedFileInfo < ' a > {
661+ struct EmbeddedFileInfo {
662662 /// When creating a `Router`, we need the API path/route to the
663663 /// target file. If creating a `Handler`, this is not needed since
664664 /// the router is responsible for the file's path on the server.
665- entry_path : Option < & ' a str > ,
665+ entry_path : Option < String > ,
666666 content_type : String ,
667667 etag_str : String ,
668668 lit_byte_str_contents : LitByteStr ,
@@ -671,9 +671,9 @@ struct EmbeddedFileInfo<'a> {
671671 cache_busted : bool ,
672672}
673673
674- impl < ' a > EmbeddedFileInfo < ' a > {
674+ impl EmbeddedFileInfo {
675675 fn from_path (
676- pathbuf : & ' a PathBuf ,
676+ pathbuf : & PathBuf ,
677677 assets_dir_abs_str : Option < & str > ,
678678 should_compress : & LitBool ,
679679 should_strip_html_ext : & LitBool ,
@@ -695,18 +695,18 @@ impl<'a> EmbeddedFileInfo<'a> {
695695
696696 // entry_path is only needed for the router (embed_assets!)
697697 let entry_path = if let Some ( dir) = assets_dir_abs_str {
698- if should_strip_html_ext. value && content_type == "text/html" {
699- Some (
700- strip_html_ext ( pathbuf) ?
701- . strip_prefix ( dir)
702- . unwrap_or_default ( ) ,
703- )
698+ let relative_entry = pathbuf
699+ . strip_prefix ( dir)
700+ . ok ( )
701+ . and_then ( |p| p. to_str ( ) )
702+ . unwrap_or_default ( ) ;
703+ let relative_path = if should_strip_html_ext. value && content_type == "text/html" {
704+ strip_html_ext_relative ( relative_entry)
704705 } else {
705- pathbuf
706- . to_str ( )
707- . ok_or ( Error :: InvalidUnicodeInEntryName ) ?
708- . strip_prefix ( dir)
709- }
706+ relative_entry. to_owned ( )
707+ } ;
708+
709+ Some ( normalize_web_path ( & relative_path) )
710710 } else {
711711 None
712712 } ;
@@ -820,21 +820,57 @@ fn etag(contents: &[u8]) -> String {
820820 format ! ( "\" {hash:016x}\" " )
821821}
822822
823- fn strip_html_ext ( entry : & Path ) -> Result < & str , Error > {
824- let entry_str = entry. to_str ( ) . ok_or ( Error :: InvalidUnicodeInEntryName ) ?;
825- let mut output = entry_str;
823+ /// Normalize a relative asset path, strip `.html`/`.htm`, and map
824+ /// `/index(.html|.htm)` to its directory route.
825+ ///
826+ /// The input is normalized via `Path::components()` so separator style
827+ /// differences across platforms do not affect route generation.
828+ fn strip_html_ext_relative ( entry : & str ) -> String {
829+ let mut output = Path :: new ( entry)
830+ . components ( )
831+ . filter_map ( |component| match component {
832+ std:: path:: Component :: Normal ( segment) => segment. to_str ( ) ,
833+ std:: path:: Component :: CurDir
834+ | std:: path:: Component :: ParentDir
835+ | std:: path:: Component :: RootDir
836+ | std:: path:: Component :: Prefix ( _) => None ,
837+ } )
838+ . collect :: < Vec < _ > > ( )
839+ . join ( "/" ) ;
826840
827841 // Strip the extension
828842 if let Some ( prefix) = output. strip_suffix ( ".html" ) {
829- output = prefix;
843+ output = prefix. to_owned ( ) ;
830844 } else if let Some ( prefix) = output. strip_suffix ( ".htm" ) {
831- output = prefix;
845+ output = prefix. to_owned ( ) ;
832846 }
833847
834848 // If it was `/index.html` or `/index.htm`, also remove `index`
835849 if output. ends_with ( "/index" ) {
836- output = output. strip_suffix ( "index" ) . unwrap_or ( "/" ) ;
850+ output = output. strip_suffix ( "index" ) . unwrap_or ( "/" ) . to_owned ( ) ;
851+ } else if output == "index" {
852+ output. clear ( ) ;
837853 }
838854
839- Ok ( output)
855+ output
856+ }
857+
858+ /// Convert a relative filesystem-style path into a rooted web route.
859+ ///
860+ /// Path segments are normalized via `Path::components()`. The returned
861+ /// route is always absolute (starts with `/`) and defaults to `/` for
862+ /// empty input.
863+ fn normalize_web_path ( relative_path : & str ) -> String {
864+ let normalized = Path :: new ( relative_path)
865+ . components ( )
866+ . filter_map ( |component| match component {
867+ std:: path:: Component :: Normal ( segment) => segment. to_str ( ) ,
868+ std:: path:: Component :: CurDir
869+ | std:: path:: Component :: ParentDir
870+ | std:: path:: Component :: RootDir
871+ | std:: path:: Component :: Prefix ( _) => None ,
872+ } )
873+ . collect :: < Vec < _ > > ( )
874+ . join ( "/" ) ;
875+ format ! ( "/{normalized}" )
840876}
0 commit comments