Skip to content

Commit e9c744b

Browse files
committed
refactor(frontmatter): Track spans
This will make error reporting easier as well as editing
1 parent 3184f03 commit e9c744b

File tree

1 file changed

+33
-21
lines changed

1 file changed

+33
-21
lines changed

src/cargo/util/frontmatter.rs

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,40 @@
11
use crate::CargoResult;
22

3+
type Span = std::ops::Range<usize>;
4+
35
#[derive(Debug)]
46
pub struct ScriptSource<'s> {
5-
shebang: Option<&'s str>,
6-
info: Option<&'s str>,
7-
frontmatter: Option<&'s str>,
8-
content: &'s str,
7+
raw: &'s str,
8+
shebang: Option<Span>,
9+
info: Option<Span>,
10+
frontmatter: Option<Span>,
11+
content: Span,
912
}
1013

1114
impl<'s> ScriptSource<'s> {
12-
pub fn parse(input: &'s str) -> CargoResult<Self> {
15+
pub fn parse(raw: &'s str) -> CargoResult<Self> {
1316
use winnow::stream::FindSlice as _;
17+
use winnow::stream::Location as _;
18+
use winnow::stream::Offset as _;
1419
use winnow::stream::Stream as _;
1520

21+
let content_end = raw.len();
1622
let mut source = Self {
23+
raw,
1724
shebang: None,
1825
info: None,
1926
frontmatter: None,
20-
content: input,
27+
content: 0..content_end,
2128
};
2229

23-
let mut input = winnow::stream::LocatingSlice::new(input);
30+
let mut input = winnow::stream::LocatingSlice::new(raw);
2431

2532
if let Some(shebang_end) = strip_shebang(input.as_ref()) {
26-
source.shebang = Some(input.next_slice(shebang_end));
27-
source.content = input.as_ref();
33+
let shebang_start = input.current_token_start();
34+
let _ = input.next_slice(shebang_end);
35+
let shebang_end = input.current_token_start();
36+
source.shebang = Some(shebang_start..shebang_end);
37+
source.content = shebang_end..content_end;
2838
}
2939

3040
// Whitespace may precede a frontmatter but must end with a newline
@@ -58,35 +68,37 @@ impl<'s> ScriptSource<'s> {
5868
let info = input.next_slice(info_nl.start);
5969
let info = info.trim_matches(is_whitespace);
6070
if !info.is_empty() {
61-
source.info = Some(info);
71+
let info_start = info.offset_from(&raw);
72+
let info_end = info_start + info.len();
73+
source.info = Some(info_start..info_end);
6274
}
6375

6476
// Ends with a line that starts with a matching number of `-` only followed by whitespace
6577
let nl_fence_pattern = format!("\n{fence_pattern}");
6678
let Some(frontmatter_nl) = input.find_slice(nl_fence_pattern.as_str()) else {
6779
anyhow::bail!("no closing `{fence_pattern}` found for frontmatter");
6880
};
69-
let frontmatter = input.next_slice(frontmatter_nl.start + 1);
70-
let frontmatter = frontmatter
71-
.strip_prefix('\n')
72-
.expect("earlier `found` + `split_at` left us here");
73-
source.frontmatter = Some(frontmatter);
81+
let frontmatter_start = input.current_token_start() + 1; // skip nl from infostring
82+
let _ = input.next_slice(frontmatter_nl.start + 1);
83+
let frontmatter_end = input.current_token_start();
84+
source.frontmatter = Some(frontmatter_start..frontmatter_end);
7485
let _ = input.next_slice(fence_length);
7586

7687
let nl = input.find_slice("\n");
7788
let after_closing_fence = input.next_slice(
7889
nl.map(|span| span.end)
7990
.unwrap_or_else(|| input.eof_offset()),
8091
);
92+
let content_start = input.current_token_start();
8193
let after_closing_fence = after_closing_fence.trim_matches(is_whitespace);
8294
if !after_closing_fence.is_empty() {
8395
// extra characters beyond the original fence pattern, even if they are extra `-`
8496
anyhow::bail!("trailing characters found after frontmatter close");
8597
}
8698

87-
source.content = input.finish();
99+
source.content = content_start..content_end;
88100

89-
let repeat = Self::parse(source.content)?;
101+
let repeat = Self::parse(source.content())?;
90102
if repeat.frontmatter.is_some() {
91103
anyhow::bail!("only one frontmatter is supported");
92104
}
@@ -95,19 +107,19 @@ impl<'s> ScriptSource<'s> {
95107
}
96108

97109
pub fn shebang(&self) -> Option<&'s str> {
98-
self.shebang
110+
self.shebang.clone().map(|span| &self.raw[span])
99111
}
100112

101113
pub fn info(&self) -> Option<&'s str> {
102-
self.info
114+
self.info.clone().map(|span| &self.raw[span])
103115
}
104116

105117
pub fn frontmatter(&self) -> Option<&'s str> {
106-
self.frontmatter
118+
self.frontmatter.clone().map(|span| &self.raw[span])
107119
}
108120

109121
pub fn content(&self) -> &'s str {
110-
self.content
122+
&self.raw[self.content.clone()]
111123
}
112124
}
113125

0 commit comments

Comments
 (0)