@@ -78,59 +78,70 @@ impl<'s> ScriptSource<'s> {
7878 source. content = content;
7979 }
8080
81- const FENCE_CHAR : char = '-' ;
82-
8381 let mut rest = source. content ;
84- while !rest. is_empty ( ) {
85- let without_spaces = rest. trim_start_matches ( [ ' ' , '\t' ] ) ;
86- let without_nl = without_spaces. trim_start_matches ( [ '\r' , '\n' ] ) ;
87- if without_nl == rest {
88- // nothing trimmed
89- break ;
90- } else if without_nl == without_spaces {
91- // frontmatter must come after a newline
82+
83+ // Whitespace may precede a frontmatter but must end with a newline
84+ const WHITESPACE : [ char ; 4 ] = [ ' ' , '\t' , '\r' , '\n' ] ;
85+ let trimmed = rest. trim_start_matches ( WHITESPACE ) ;
86+ if trimmed. len ( ) != rest. len ( ) {
87+ let trimmed_len = rest. len ( ) - trimmed. len ( ) ;
88+ let last_trimmed_index = trimmed_len - 1 ;
89+ if rest. as_bytes ( ) [ last_trimmed_index] != b'\n' {
90+ // either not a frontmatter or invalid opening
9291 return Ok ( source) ;
9392 }
94- rest = without_nl;
9593 }
96- let fence_end = rest
94+ rest = trimmed;
95+
96+ // Opens with a line that starts with 3 or more `-` followed by an optional identifier
97+ const FENCE_CHAR : char = '-' ;
98+ let fence_length = rest
9799 . char_indices ( )
98100 . find_map ( |( i, c) | ( c != FENCE_CHAR ) . then_some ( i) )
99- . unwrap_or ( source . content . len ( ) ) ;
100- let ( fence_pattern , rest ) = match fence_end {
101+ . unwrap_or ( rest . len ( ) ) ;
102+ match fence_length {
101103 0 => {
102104 return Ok ( source) ;
103105 }
104106 1 | 2 => {
107+ // either not a frontmatter or invalid frontmatter opening
105108 anyhow:: bail!(
106- "found {fence_end } `{FENCE_CHAR}` in rust frontmatter, expected at least 3"
109+ "found {fence_length } `{FENCE_CHAR}` in rust frontmatter, expected at least 3"
107110 )
108111 }
109- _ => rest. split_at ( fence_end) ,
112+ _ => { }
113+ }
114+ let ( fence_pattern, rest) = rest. split_at ( fence_length) ;
115+ let Some ( info_end_index) = rest. find ( '\n' ) else {
116+ anyhow:: bail!( "no closing `{fence_pattern}` found for frontmatter" ) ;
110117 } ;
111- let nl_fence_pattern = format ! ( "\n {fence_pattern}" ) ;
112- let ( info, content) = rest. split_once ( "\n " ) . unwrap_or ( ( rest, "" ) ) ;
113- let info = info. trim ( ) ;
118+ let ( info, rest) = rest. split_at ( info_end_index) ;
119+ let info = info. trim_matches ( WHITESPACE ) ;
114120 if !info. is_empty ( ) {
115121 source. info = Some ( info) ;
116122 }
117- source. content = content;
123+ let rest = rest
124+ . strip_prefix ( '\n' )
125+ . expect ( "earlier `found` + `split_at` left us here" ) ;
118126
119- let Some ( frontmatter_nl) = source. content . find ( & nl_fence_pattern) else {
127+ // Ends with a line that starts with a matching number of `-` only followed by whitespace
128+ let nl_fence_pattern = format ! ( "\n {fence_pattern}" ) ;
129+ let Some ( frontmatter_nl) = rest. find ( & nl_fence_pattern) else {
120130 anyhow:: bail!( "no closing `{fence_pattern}` found for frontmatter" ) ;
121131 } ;
122- source. frontmatter = Some ( & source. content [ ..frontmatter_nl + 1 ] ) ;
123- source. content = & source. content [ frontmatter_nl + nl_fence_pattern. len ( ) ..] ;
124-
125- let ( line, content) = source
126- . content
127- . split_once ( "\n " )
128- . unwrap_or ( ( source. content , "" ) ) ;
129- let line = line. trim ( ) ;
130- if !line. is_empty ( ) {
131- anyhow:: bail!( "unexpected trailing content on closing fence: `{line}`" ) ;
132+ let frontmatter = & rest[ ..frontmatter_nl + 1 ] ;
133+ let rest = & rest[ frontmatter_nl + nl_fence_pattern. len ( ) ..] ;
134+ source. frontmatter = Some ( frontmatter) ;
135+
136+ let ( after_closing_fence, rest) = rest. split_once ( "\n " ) . unwrap_or ( ( rest, "" ) ) ;
137+ let after_closing_fence = after_closing_fence. trim_matches ( WHITESPACE ) ;
138+ if !after_closing_fence. is_empty ( ) {
139+ // extra characters beyond the original fence pattern, even if they are extra `-`
140+ anyhow:: bail!( "trailing characters found after frontmatter close" ) ;
132141 }
133- source. content = content;
142+
143+ let frontmatter_len = input. len ( ) - rest. len ( ) ;
144+ source. content = & input[ frontmatter_len..] ;
134145
135146 Ok ( source)
136147 }
@@ -466,7 +477,7 @@ content: "\n// infostrings can only be a single identifier.\n\nfn main() {}\n"
466477fn main() {}
467478"# ,
468479 ) ,
469- str![ "unexpected trailing content on closing fence: `-` " ] ,
480+ str![ "trailing characters found after frontmatter close " ] ,
470481 ) ;
471482 }
472483
@@ -905,7 +916,7 @@ content: "\nfn main() {}\n"
905916fn main() {}
906917"# ,
907918 ) ,
908- str![ "unexpected trailing content on closing fence: `--` " ] ,
919+ str![ "trailing characters found after frontmatter close " ] ,
909920 ) ;
910921 }
911922
@@ -942,7 +953,7 @@ time="0.1.25"
942953fn main() {}
943954"# ,
944955 ) ,
945- str![ "unexpected trailing content on closing fence: `-` " ] ,
956+ str![ "trailing characters found after frontmatter close " ] ,
946957 ) ;
947958 }
948959
0 commit comments