Skip to content

Commit 5bf74df

Browse files
refactor(detect): reduce complexity of parse_header_line in detect.rs (#1540)
Extract parse_header_value helper to eliminate the nested quoted-string parser from parse_header_line. The quoted-value logic (escape handling, closing-quote scan, tail slicing) was inlined inside the source= branch, adding ~4 levels of nesting. Moving it into a dedicated private function reduces parse_header_line to a flat dispatch loop with no deep nesting. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 3b13e35 commit 5bf74df

1 file changed

Lines changed: 38 additions & 31 deletions

File tree

src/detect.rs

Lines changed: 38 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -234,37 +234,9 @@ pub fn parse_header_line(line: &str) -> Option<HeaderMetadata> {
234234
while !remaining.is_empty() {
235235
remaining = remaining.trim_start();
236236
if let Some(after) = remaining.strip_prefix("source=") {
237-
// Handle quoted value: source="path with spaces"
238-
if let Some(after_quote) = after.strip_prefix('"') {
239-
// Find closing quote, skipping escaped quotes (\")
240-
let mut value = String::new();
241-
let mut chars = after_quote.char_indices();
242-
let mut end_pos = after_quote.len();
243-
while let Some((i, c)) = chars.next() {
244-
if c == '\\' {
245-
// Consume the escaped character
246-
if let Some((_, escaped)) = chars.next() {
247-
value.push(escaped);
248-
}
249-
} else if c == '"' {
250-
end_pos = i;
251-
break;
252-
} else {
253-
value.push(c);
254-
}
255-
}
256-
source = value;
257-
remaining = if end_pos < after_quote.len() {
258-
&after_quote[end_pos + 1..]
259-
} else {
260-
""
261-
};
262-
} else {
263-
// Unquoted: take until next whitespace
264-
let end = after.find(' ').unwrap_or(after.len());
265-
source = after[..end].to_string();
266-
remaining = &after[end..];
267-
}
237+
let (value, tail) = parse_header_value(after);
238+
source = value;
239+
remaining = tail;
268240
} else if let Some(after) = remaining.strip_prefix("version=") {
269241
let end = after.find(' ').unwrap_or(after.len());
270242
version = after[..end].to_string();
@@ -283,6 +255,41 @@ pub fn parse_header_line(line: &str) -> Option<HeaderMetadata> {
283255
Some(HeaderMetadata { source, version })
284256
}
285257

258+
/// Parse a header field value that is either quoted (`"…"`) or unquoted (up to whitespace).
259+
///
260+
/// Quoted values support backslash-escaped characters (e.g. `\"`).
261+
/// Returns `(value, remaining_input)`.
262+
fn parse_header_value(input: &str) -> (String, &str) {
263+
if let Some(after_quote) = input.strip_prefix('"') {
264+
// Quoted value: scan until closing `"`, honouring `\"` escapes.
265+
let mut value = String::new();
266+
let mut chars = after_quote.char_indices();
267+
let mut end_pos = after_quote.len();
268+
while let Some((i, c)) = chars.next() {
269+
if c == '\\' {
270+
if let Some((_, escaped)) = chars.next() {
271+
value.push(escaped);
272+
}
273+
} else if c == '"' {
274+
end_pos = i;
275+
break;
276+
} else {
277+
value.push(c);
278+
}
279+
}
280+
let tail = if end_pos < after_quote.len() {
281+
&after_quote[end_pos + 1..]
282+
} else {
283+
""
284+
};
285+
(value, tail)
286+
} else {
287+
// Unquoted: take until next whitespace.
288+
let end = input.find(' ').unwrap_or(input.len());
289+
(input[..end].to_string(), &input[end..])
290+
}
291+
}
292+
286293
#[cfg(test)]
287294
mod tests {
288295
use super::*;

0 commit comments

Comments
 (0)