@@ -146,59 +146,70 @@ impl<'s> ScriptSource<'s> {
146146 source. content = content;
147147 }
148148
149- const FENCE_CHAR : char = '-' ;
150-
151149 let mut rest = source. content ;
152- while !rest. is_empty ( ) {
153- let without_spaces = rest. trim_start_matches ( [ ' ' , '\t' ] ) ;
154- let without_nl = without_spaces. trim_start_matches ( [ '\r' , '\n' ] ) ;
155- if without_nl == rest {
156- // nothing trimmed
157- break ;
158- } else if without_nl == without_spaces {
159- // frontmatter must come after a newline
150+
151+ // Whitespace may precede a frontmatter but must end with a newline
152+ const WHITESPACE : [ char ; 4 ] = [ ' ' , '\t' , '\r' , '\n' ] ;
153+ let trimmed = rest. trim_start_matches ( WHITESPACE ) ;
154+ if trimmed. len ( ) != rest. len ( ) {
155+ let trimmed_len = rest. len ( ) - trimmed. len ( ) ;
156+ let last_trimmed_index = trimmed_len - 1 ;
157+ if rest. as_bytes ( ) [ last_trimmed_index] != b'\n' {
158+ // either not a frontmatter or invalid opening
160159 return Ok ( source) ;
161160 }
162- rest = without_nl;
163161 }
164- let fence_end = rest
162+ rest = trimmed;
163+
164+ // Opens with a line that starts with 3 or more `-` followed by an optional identifier
165+ const FENCE_CHAR : char = '-' ;
166+ let fence_length = rest
165167 . char_indices ( )
166168 . find_map ( |( i, c) | ( c != FENCE_CHAR ) . then_some ( i) )
167- . unwrap_or ( source . content . len ( ) ) ;
168- let ( fence_pattern , rest ) = match fence_end {
169+ . unwrap_or ( rest . len ( ) ) ;
170+ match fence_length {
169171 0 => {
170172 return Ok ( source) ;
171173 }
172174 1 | 2 => {
175+ // either not a frontmatter or invalid frontmatter opening
173176 anyhow:: bail!(
174- "found {fence_end } `{FENCE_CHAR}` in rust frontmatter, expected at least 3"
177+ "found {fence_length } `{FENCE_CHAR}` in rust frontmatter, expected at least 3"
175178 )
176179 }
177- _ => rest. split_at ( fence_end) ,
180+ _ => { }
181+ }
182+ let ( fence_pattern, rest) = rest. split_at ( fence_length) ;
183+ let Some ( info_end_index) = rest. find ( '\n' ) else {
184+ anyhow:: bail!( "no closing `{fence_pattern}` found for frontmatter" ) ;
178185 } ;
179- let nl_fence_pattern = format ! ( "\n {fence_pattern}" ) ;
180- let ( info, content) = rest. split_once ( "\n " ) . unwrap_or ( ( rest, "" ) ) ;
181- let info = info. trim ( ) ;
186+ let ( info, rest) = rest. split_at ( info_end_index) ;
187+ let info = info. trim_matches ( WHITESPACE ) ;
182188 if !info. is_empty ( ) {
183189 source. info = Some ( info) ;
184190 }
185- source. content = content;
191+ let rest = rest
192+ . strip_prefix ( '\n' )
193+ . expect ( "earlier `found` + `split_at` left us here" ) ;
186194
187- let Some ( frontmatter_nl) = source. content . find ( & nl_fence_pattern) else {
195+ // Ends with a line that starts with a matching number of `-` only followed by whitespace
196+ let nl_fence_pattern = format ! ( "\n {fence_pattern}" ) ;
197+ let Some ( frontmatter_nl) = rest. find ( & nl_fence_pattern) else {
188198 anyhow:: bail!( "no closing `{fence_pattern}` found for frontmatter" ) ;
189199 } ;
190- source. frontmatter = Some ( & source. content [ ..frontmatter_nl + 1 ] ) ;
191- source. content = & source. content [ frontmatter_nl + nl_fence_pattern. len ( ) ..] ;
192-
193- let ( line, content) = source
194- . content
195- . split_once ( "\n " )
196- . unwrap_or ( ( source. content , "" ) ) ;
197- let line = line. trim ( ) ;
198- if !line. is_empty ( ) {
199- anyhow:: bail!( "unexpected trailing content on closing fence: `{line}`" ) ;
200+ let frontmatter = & rest[ ..frontmatter_nl + 1 ] ;
201+ let rest = & rest[ frontmatter_nl + nl_fence_pattern. len ( ) ..] ;
202+ source. frontmatter = Some ( frontmatter) ;
203+
204+ let ( after_closing_fence, rest) = rest. split_once ( "\n " ) . unwrap_or ( ( rest, "" ) ) ;
205+ let after_closing_fence = after_closing_fence. trim_matches ( WHITESPACE ) ;
206+ if !after_closing_fence. is_empty ( ) {
207+ // extra characters beyond the original fence pattern, even if they are extra `-`
208+ anyhow:: bail!( "trailing characters found after frontmatter close" ) ;
200209 }
201- source. content = content;
210+
211+ let frontmatter_len = input. len ( ) - rest. len ( ) ;
212+ source. content = & input[ frontmatter_len..] ;
202213
203214 Ok ( source)
204215 }
@@ -534,7 +545,7 @@ content: "\n// infostrings can only be a single identifier.\n\nfn main() {}\n"
534545fn main() {}
535546"# ,
536547 ) ,
537- str![ "unexpected trailing content on closing fence: `-` " ] ,
548+ str![ "trailing characters found after frontmatter close " ] ,
538549 ) ;
539550 }
540551
@@ -973,7 +984,7 @@ content: "\nfn main() {}\n"
973984fn main() {}
974985"# ,
975986 ) ,
976- str![ "unexpected trailing content on closing fence: `--` " ] ,
987+ str![ "trailing characters found after frontmatter close " ] ,
977988 ) ;
978989 }
979990
@@ -1010,7 +1021,7 @@ time="0.1.25"
10101021fn main() {}
10111022"# ,
10121023 ) ,
1013- str![ "unexpected trailing content on closing fence: `-` " ] ,
1024+ str![ "trailing characters found after frontmatter close " ] ,
10141025 ) ;
10151026 }
10161027
0 commit comments