@@ -78,59 +78,70 @@ impl<'s> ScriptSource<'s> {
78
78
source. content = content;
79
79
}
80
80
81
- const FENCE_CHAR : char = '-' ;
82
-
83
81
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
92
91
return Ok ( source) ;
93
92
}
94
- rest = without_nl;
95
93
}
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
97
99
. char_indices ( )
98
100
. 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 {
101
103
0 => {
102
104
return Ok ( source) ;
103
105
}
104
106
1 | 2 => {
107
+ // either not a frontmatter or invalid frontmatter opening
105
108
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"
107
110
)
108
111
}
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" ) ;
110
117
} ;
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 ) ;
114
120
if !info. is_empty ( ) {
115
121
source. info = Some ( info) ;
116
122
}
117
- source. content = content;
123
+ let rest = rest
124
+ . strip_prefix ( '\n' )
125
+ . expect ( "earlier `found` + `split_at` left us here" ) ;
118
126
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 {
120
130
anyhow:: bail!( "no closing `{fence_pattern}` found for frontmatter" ) ;
121
131
} ;
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" ) ;
132
141
}
133
- source. content = content;
142
+
143
+ let frontmatter_len = input. len ( ) - rest. len ( ) ;
144
+ source. content = & input[ frontmatter_len..] ;
134
145
135
146
Ok ( source)
136
147
}
@@ -466,7 +477,7 @@ content: "\n// infostrings can only be a single identifier.\n\nfn main() {}\n"
466
477
fn main() {}
467
478
"# ,
468
479
) ,
469
- str![ "unexpected trailing content on closing fence: `-` " ] ,
480
+ str![ "trailing characters found after frontmatter close " ] ,
470
481
) ;
471
482
}
472
483
@@ -905,7 +916,7 @@ content: "\nfn main() {}\n"
905
916
fn main() {}
906
917
"# ,
907
918
) ,
908
- str![ "unexpected trailing content on closing fence: `--` " ] ,
919
+ str![ "trailing characters found after frontmatter close " ] ,
909
920
) ;
910
921
}
911
922
@@ -942,7 +953,7 @@ time="0.1.25"
942
953
fn main() {}
943
954
"# ,
944
955
) ,
945
- str![ "unexpected trailing content on closing fence: `-` " ] ,
956
+ str![ "trailing characters found after frontmatter close " ] ,
946
957
) ;
947
958
}
948
959
0 commit comments