@@ -21,7 +21,7 @@ pub(super) fn expand_manifest(
2121 path : & std:: path:: Path ,
2222 gctx : & GlobalContext ,
2323) -> CargoResult < String > {
24- let source = split_source ( content) ?;
24+ let source = ScriptSource :: parse ( content) ?;
2525 if let Some ( frontmatter) = source. frontmatter {
2626 match source. info {
2727 Some ( "cargo" ) | None => { }
@@ -196,87 +196,89 @@ struct ScriptSource<'s> {
196196 content : & ' s str ,
197197}
198198
199- fn split_source ( input : & str ) -> CargoResult < ScriptSource < ' _ > > {
200- let mut source = ScriptSource {
201- shebang : None ,
202- info : None ,
203- frontmatter : None ,
204- content : input,
205- } ;
199+ impl < ' s > ScriptSource < ' s > {
200+ fn parse ( input : & ' s str ) -> CargoResult < Self > {
201+ let mut source = Self {
202+ shebang : None ,
203+ info : None ,
204+ frontmatter : None ,
205+ content : input,
206+ } ;
207+
208+ // See rust-lang/rust's compiler/rustc_lexer/src/lib.rs's `strip_shebang`
209+ // Shebang must start with `#!` literally, without any preceding whitespace.
210+ // For simplicity we consider any line starting with `#!` a shebang,
211+ // regardless of restrictions put on shebangs by specific platforms.
212+ if let Some ( rest) = source. content . strip_prefix ( "#!" ) {
213+ // Ok, this is a shebang but if the next non-whitespace token is `[`,
214+ // then it may be valid Rust code, so consider it Rust code.
215+ if rest. trim_start ( ) . starts_with ( '[' ) {
216+ return Ok ( source) ;
217+ }
206218
207- // See rust-lang/rust's compiler/rustc_lexer/src/lib.rs's `strip_shebang`
208- // Shebang must start with `#!` literally, without any preceding whitespace.
209- // For simplicity we consider any line starting with `#!` a shebang,
210- // regardless of restrictions put on shebangs by specific platforms.
211- if let Some ( rest ) = source . content . strip_prefix ( "#!" ) {
212- // Ok, this is a shebang but if the next non-whitespace token is `[`,
213- // then it may be valid Rust code, so consider it Rust code.
214- if rest . trim_start ( ) . starts_with ( '[' ) {
215- return Ok ( source) ;
219+ // No other choice than to consider this a shebang.
220+ let newline_end = source
221+ . content
222+ . find ( '\n' )
223+ . map ( |pos| pos + 1 )
224+ . unwrap_or ( source . content . len ( ) ) ;
225+ let ( shebang , content ) = source . content . split_at ( newline_end ) ;
226+ source . shebang = Some ( shebang ) ;
227+ source. content = content ;
216228 }
217229
218- // No other choice than to consider this a shebang.
219- let newline_end = source
220- . content
221- . find ( '\n' )
222- . map ( |pos| pos + 1 )
230+ const FENCE_CHAR : char = '-' ;
231+
232+ let mut trimmed_content = source. content ;
233+ while !trimmed_content. is_empty ( ) {
234+ let c = trimmed_content;
235+ let c = c. trim_start_matches ( [ ' ' , '\t' ] ) ;
236+ let c = c. trim_start_matches ( [ '\r' , '\n' ] ) ;
237+ if c == trimmed_content {
238+ break ;
239+ }
240+ trimmed_content = c;
241+ }
242+ let fence_end = trimmed_content
243+ . char_indices ( )
244+ . find_map ( |( i, c) | ( c != FENCE_CHAR ) . then_some ( i) )
223245 . unwrap_or ( source. content . len ( ) ) ;
224- let ( shebang, content) = source. content . split_at ( newline_end) ;
225- source. shebang = Some ( shebang) ;
246+ let ( fence_pattern, rest) = match fence_end {
247+ 0 => {
248+ return Ok ( source) ;
249+ }
250+ 1 | 2 => {
251+ anyhow:: bail!(
252+ "found {fence_end} `{FENCE_CHAR}` in rust frontmatter, expected at least 3"
253+ )
254+ }
255+ _ => trimmed_content. split_at ( fence_end) ,
256+ } ;
257+ let ( info, content) = rest. split_once ( "\n " ) . unwrap_or ( ( rest, "" ) ) ;
258+ let info = info. trim ( ) ;
259+ if !info. is_empty ( ) {
260+ source. info = Some ( info) ;
261+ }
226262 source. content = content;
227- }
228263
229- const FENCE_CHAR : char = '-' ;
264+ let Some ( ( frontmatter, content) ) = source. content . split_once ( fence_pattern) else {
265+ anyhow:: bail!( "no closing `{fence_pattern}` found for frontmatter" ) ;
266+ } ;
267+ source. frontmatter = Some ( frontmatter) ;
268+ source. content = content;
230269
231- let mut trimmed_content = source. content ;
232- while !trimmed_content. is_empty ( ) {
233- let c = trimmed_content;
234- let c = c. trim_start_matches ( [ ' ' , '\t' ] ) ;
235- let c = c. trim_start_matches ( [ '\r' , '\n' ] ) ;
236- if c == trimmed_content {
237- break ;
238- }
239- trimmed_content = c;
240- }
241- let fence_end = trimmed_content
242- . char_indices ( )
243- . find_map ( |( i, c) | ( c != FENCE_CHAR ) . then_some ( i) )
244- . unwrap_or ( source. content . len ( ) ) ;
245- let ( fence_pattern, rest) = match fence_end {
246- 0 => {
247- return Ok ( source) ;
248- }
249- 1 | 2 => {
250- anyhow:: bail!(
251- "found {fence_end} `{FENCE_CHAR}` in rust frontmatter, expected at least 3"
252- )
270+ let ( line, content) = source
271+ . content
272+ . split_once ( "\n " )
273+ . unwrap_or ( ( source. content , "" ) ) ;
274+ let line = line. trim ( ) ;
275+ if !line. is_empty ( ) {
276+ anyhow:: bail!( "unexpected trailing content on closing fence: `{line}`" ) ;
253277 }
254- _ => trimmed_content. split_at ( fence_end) ,
255- } ;
256- let ( info, content) = rest. split_once ( "\n " ) . unwrap_or ( ( rest, "" ) ) ;
257- let info = info. trim ( ) ;
258- if !info. is_empty ( ) {
259- source. info = Some ( info) ;
260- }
261- source. content = content;
278+ source. content = content;
262279
263- let Some ( ( frontmatter, content) ) = source. content . split_once ( fence_pattern) else {
264- anyhow:: bail!( "no closing `{fence_pattern}` found for frontmatter" ) ;
265- } ;
266- source. frontmatter = Some ( frontmatter) ;
267- source. content = content;
268-
269- let ( line, content) = source
270- . content
271- . split_once ( "\n " )
272- . unwrap_or ( ( source. content , "" ) ) ;
273- let line = line. trim ( ) ;
274- if !line. is_empty ( ) {
275- anyhow:: bail!( "unexpected trailing content on closing fence: `{line}`" ) ;
280+ Ok ( source)
276281 }
277- source. content = content;
278-
279- Ok ( source)
280282}
281283
282284#[ cfg( test) ]
@@ -291,7 +293,7 @@ mod test_expand {
291293 fn assert_source ( source : & str , expected : impl IntoData ) {
292294 use std:: fmt:: Write as _;
293295
294- let actual = match split_source ( source) {
296+ let actual = match ScriptSource :: parse ( source) {
295297 Ok ( actual) => actual,
296298 Err ( err) => panic ! ( "unexpected err: {err}" ) ,
297299 } ;
@@ -497,7 +499,7 @@ content: "\nfn main() {}"
497499 #[ test]
498500 fn split_too_few_dashes ( ) {
499501 assert_err (
500- split_source (
502+ ScriptSource :: parse (
501503 r#"#!/usr/bin/env cargo
502504--
503505[dependencies]
@@ -513,7 +515,7 @@ fn main() {}
513515 #[ test]
514516 fn split_mismatched_dashes ( ) {
515517 assert_err (
516- split_source (
518+ ScriptSource :: parse (
517519 r#"#!/usr/bin/env cargo
518520---
519521[dependencies]
@@ -529,7 +531,7 @@ fn main() {}
529531 #[ test]
530532 fn split_missing_close ( ) {
531533 assert_err (
532- split_source (
534+ ScriptSource :: parse (
533535 r#"#!/usr/bin/env cargo
534536---
535537[dependencies]
0 commit comments