@@ -21,9 +21,9 @@ pub(super) fn expand_manifest(
21
21
path : & std:: path:: Path ,
22
22
gctx : & GlobalContext ,
23
23
) -> CargoResult < String > {
24
- let source = split_source ( content) ?;
25
- if let Some ( frontmatter) = source. frontmatter {
26
- match source. info {
24
+ let source = ScriptSource :: parse ( content) ?;
25
+ if let Some ( frontmatter) = source. frontmatter ( ) {
26
+ match source. info ( ) {
27
27
Some ( "cargo" ) | None => { }
28
28
Some ( other) => {
29
29
if let Some ( remainder) = other. strip_prefix ( "cargo," ) {
@@ -50,15 +50,15 @@ pub(super) fn expand_manifest(
50
50
)
51
51
. into_path_unlocked ( ) ;
52
52
let mut hacked_source = String :: new ( ) ;
53
- if let Some ( shebang) = source. shebang {
53
+ if let Some ( shebang) = source. shebang ( ) {
54
54
writeln ! ( hacked_source, "{shebang}" ) ?;
55
55
}
56
56
writeln ! ( hacked_source) ?; // open
57
57
for _ in 0 ..frontmatter. lines ( ) . count ( ) {
58
58
writeln ! ( hacked_source) ?;
59
59
}
60
60
writeln ! ( hacked_source) ?; // close
61
- writeln ! ( hacked_source, "{}" , source. content) ?;
61
+ writeln ! ( hacked_source, "{}" , source. content( ) ) ?;
62
62
if let Some ( parent) = hacked_path. parent ( ) {
63
63
cargo_util:: paths:: create_dir_all ( parent) ?;
64
64
}
@@ -189,94 +189,112 @@ fn sanitize_name(name: &str) -> String {
189
189
}
190
190
191
191
#[ derive( Debug ) ]
192
- struct Source < ' s > {
192
+ pub struct ScriptSource < ' s > {
193
193
shebang : Option < & ' s str > ,
194
194
info : Option < & ' s str > ,
195
195
frontmatter : Option < & ' s str > ,
196
196
content : & ' s str ,
197
197
}
198
198
199
- fn split_source ( input : & str ) -> CargoResult < Source < ' _ > > {
200
- let mut source = Source {
201
- shebang : None ,
202
- info : None ,
203
- frontmatter : None ,
204
- content : input,
205
- } ;
199
+ impl < ' s > ScriptSource < ' s > {
200
+ pub 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
+ }
206
218
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 ;
216
228
}
217
229
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) )
223
245
. 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
+ }
226
262
source. content = content;
227
- }
228
263
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;
230
269
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 ;
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}`" ) ;
238
277
}
239
- trimmed_content = c;
278
+ source. content = content;
279
+
280
+ Ok ( source)
240
281
}
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
- )
253
- }
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) ;
282
+
283
+ pub fn shebang ( & self ) -> Option < & ' s str > {
284
+ self . shebang
260
285
}
261
- source. content = content;
262
286
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}`" ) ;
287
+ pub fn info ( & self ) -> Option < & ' s str > {
288
+ self . info
276
289
}
277
- source. content = content;
278
290
279
- Ok ( source)
291
+ pub fn frontmatter ( & self ) -> Option < & ' s str > {
292
+ self . frontmatter
293
+ }
294
+
295
+ pub fn content ( & self ) -> & ' s str {
296
+ self . content
297
+ }
280
298
}
281
299
282
300
#[ cfg( test) ]
@@ -291,16 +309,16 @@ mod test_expand {
291
309
fn assert_source ( source : & str , expected : impl IntoData ) {
292
310
use std:: fmt:: Write as _;
293
311
294
- let actual = match split_source ( source) {
312
+ let actual = match ScriptSource :: parse ( source) {
295
313
Ok ( actual) => actual,
296
314
Err ( err) => panic ! ( "unexpected err: {err}" ) ,
297
315
} ;
298
316
299
317
let mut rendered = String :: new ( ) ;
300
- write_optional_field ( & mut rendered, "shebang" , actual. shebang ) ;
301
- write_optional_field ( & mut rendered, "info" , actual. info ) ;
302
- write_optional_field ( & mut rendered, "frontmatter" , actual. frontmatter ) ;
303
- writeln ! ( & mut rendered, "content: {:?}" , actual. content) . unwrap ( ) ;
318
+ write_optional_field ( & mut rendered, "shebang" , actual. shebang ( ) ) ;
319
+ write_optional_field ( & mut rendered, "info" , actual. info ( ) ) ;
320
+ write_optional_field ( & mut rendered, "frontmatter" , actual. frontmatter ( ) ) ;
321
+ writeln ! ( & mut rendered, "content: {:?}" , actual. content( ) ) . unwrap ( ) ;
304
322
assert_data_eq ! ( rendered, expected. raw( ) ) ;
305
323
}
306
324
@@ -497,7 +515,7 @@ content: "\nfn main() {}"
497
515
#[ test]
498
516
fn split_too_few_dashes ( ) {
499
517
assert_err (
500
- split_source (
518
+ ScriptSource :: parse (
501
519
r#"#!/usr/bin/env cargo
502
520
--
503
521
[dependencies]
@@ -513,7 +531,7 @@ fn main() {}
513
531
#[ test]
514
532
fn split_mismatched_dashes ( ) {
515
533
assert_err (
516
- split_source (
534
+ ScriptSource :: parse (
517
535
r#"#!/usr/bin/env cargo
518
536
---
519
537
[dependencies]
@@ -529,7 +547,7 @@ fn main() {}
529
547
#[ test]
530
548
fn split_missing_close ( ) {
531
549
assert_err (
532
- split_source (
550
+ ScriptSource :: parse (
533
551
r#"#!/usr/bin/env cargo
534
552
---
535
553
[dependencies]
0 commit comments