refactor(toml): Move the parse fn onto ScriptSource

This commit is contained in:
Ed Page 2024-11-22 16:36:22 -06:00
parent 7ec1867ba0
commit e82a4beaed

View File

@ -21,7 +21,7 @@ pub(super) fn expand_manifest(
path: &std::path::Path, path: &std::path::Path,
gctx: &GlobalContext, gctx: &GlobalContext,
) -> CargoResult<String> { ) -> CargoResult<String> {
let source = split_source(content)?; let source = ScriptSource::parse(content)?;
if let Some(frontmatter) = source.frontmatter { if let Some(frontmatter) = source.frontmatter {
match source.info { match source.info {
Some("cargo") | None => {} Some("cargo") | None => {}
@ -196,87 +196,89 @@ struct ScriptSource<'s> {
content: &'s str, content: &'s str,
} }
fn split_source(input: &str) -> CargoResult<ScriptSource<'_>> { impl<'s> ScriptSource<'s> {
let mut source = ScriptSource { fn parse(input: &'s str) -> CargoResult<Self> {
shebang: None, let mut source = Self {
info: None, shebang: None,
frontmatter: None, info: None,
content: input, frontmatter: None,
}; content: input,
};
// See rust-lang/rust's compiler/rustc_lexer/src/lib.rs's `strip_shebang` // See rust-lang/rust's compiler/rustc_lexer/src/lib.rs's `strip_shebang`
// Shebang must start with `#!` literally, without any preceding whitespace. // Shebang must start with `#!` literally, without any preceding whitespace.
// For simplicity we consider any line starting with `#!` a shebang, // For simplicity we consider any line starting with `#!` a shebang,
// regardless of restrictions put on shebangs by specific platforms. // regardless of restrictions put on shebangs by specific platforms.
if let Some(rest) = source.content.strip_prefix("#!") { if let Some(rest) = source.content.strip_prefix("#!") {
// Ok, this is a shebang but if the next non-whitespace token is `[`, // Ok, this is a shebang but if the next non-whitespace token is `[`,
// then it may be valid Rust code, so consider it Rust code. // then it may be valid Rust code, so consider it Rust code.
if rest.trim_start().starts_with('[') { if rest.trim_start().starts_with('[') {
return Ok(source); return Ok(source);
}
// No other choice than to consider this a shebang.
let newline_end = source
.content
.find('\n')
.map(|pos| pos + 1)
.unwrap_or(source.content.len());
let (shebang, content) = source.content.split_at(newline_end);
source.shebang = Some(shebang);
source.content = content;
} }
// No other choice than to consider this a shebang. const FENCE_CHAR: char = '-';
let newline_end = source
.content let mut trimmed_content = source.content;
.find('\n') while !trimmed_content.is_empty() {
.map(|pos| pos + 1) let c = trimmed_content;
let c = c.trim_start_matches([' ', '\t']);
let c = c.trim_start_matches(['\r', '\n']);
if c == trimmed_content {
break;
}
trimmed_content = c;
}
let fence_end = trimmed_content
.char_indices()
.find_map(|(i, c)| (c != FENCE_CHAR).then_some(i))
.unwrap_or(source.content.len()); .unwrap_or(source.content.len());
let (shebang, content) = source.content.split_at(newline_end); let (fence_pattern, rest) = match fence_end {
source.shebang = Some(shebang); 0 => {
return Ok(source);
}
1 | 2 => {
anyhow::bail!(
"found {fence_end} `{FENCE_CHAR}` in rust frontmatter, expected at least 3"
)
}
_ => trimmed_content.split_at(fence_end),
};
let (info, content) = rest.split_once("\n").unwrap_or((rest, ""));
let info = info.trim();
if !info.is_empty() {
source.info = Some(info);
}
source.content = content; source.content = content;
}
const FENCE_CHAR: char = '-'; let Some((frontmatter, content)) = source.content.split_once(fence_pattern) else {
anyhow::bail!("no closing `{fence_pattern}` found for frontmatter");
};
source.frontmatter = Some(frontmatter);
source.content = content;
let mut trimmed_content = source.content; let (line, content) = source
while !trimmed_content.is_empty() { .content
let c = trimmed_content; .split_once("\n")
let c = c.trim_start_matches([' ', '\t']); .unwrap_or((source.content, ""));
let c = c.trim_start_matches(['\r', '\n']); let line = line.trim();
if c == trimmed_content { if !line.is_empty() {
break; anyhow::bail!("unexpected trailing content on closing fence: `{line}`");
} }
trimmed_content = c; source.content = content;
}
let fence_end = trimmed_content
.char_indices()
.find_map(|(i, c)| (c != FENCE_CHAR).then_some(i))
.unwrap_or(source.content.len());
let (fence_pattern, rest) = match fence_end {
0 => {
return Ok(source);
}
1 | 2 => {
anyhow::bail!(
"found {fence_end} `{FENCE_CHAR}` in rust frontmatter, expected at least 3"
)
}
_ => trimmed_content.split_at(fence_end),
};
let (info, content) = rest.split_once("\n").unwrap_or((rest, ""));
let info = info.trim();
if !info.is_empty() {
source.info = Some(info);
}
source.content = content;
let Some((frontmatter, content)) = source.content.split_once(fence_pattern) else { Ok(source)
anyhow::bail!("no closing `{fence_pattern}` found for frontmatter");
};
source.frontmatter = Some(frontmatter);
source.content = content;
let (line, content) = source
.content
.split_once("\n")
.unwrap_or((source.content, ""));
let line = line.trim();
if !line.is_empty() {
anyhow::bail!("unexpected trailing content on closing fence: `{line}`");
} }
source.content = content;
Ok(source)
} }
#[cfg(test)] #[cfg(test)]
@ -291,7 +293,7 @@ mod test_expand {
fn assert_source(source: &str, expected: impl IntoData) { fn assert_source(source: &str, expected: impl IntoData) {
use std::fmt::Write as _; use std::fmt::Write as _;
let actual = match split_source(source) { let actual = match ScriptSource::parse(source) {
Ok(actual) => actual, Ok(actual) => actual,
Err(err) => panic!("unexpected err: {err}"), Err(err) => panic!("unexpected err: {err}"),
}; };
@ -497,7 +499,7 @@ content: "\nfn main() {}"
#[test] #[test]
fn split_too_few_dashes() { fn split_too_few_dashes() {
assert_err( assert_err(
split_source( ScriptSource::parse(
r#"#!/usr/bin/env cargo r#"#!/usr/bin/env cargo
-- --
[dependencies] [dependencies]
@ -513,7 +515,7 @@ fn main() {}
#[test] #[test]
fn split_mismatched_dashes() { fn split_mismatched_dashes() {
assert_err( assert_err(
split_source( ScriptSource::parse(
r#"#!/usr/bin/env cargo r#"#!/usr/bin/env cargo
--- ---
[dependencies] [dependencies]
@ -529,7 +531,7 @@ fn main() {}
#[test] #[test]
fn split_missing_close() { fn split_missing_close() {
assert_err( assert_err(
split_source( ScriptSource::parse(
r#"#!/usr/bin/env cargo r#"#!/usr/bin/env cargo
--- ---
[dependencies] [dependencies]