style(pi-shell): formatted fixup shell cleanup code and tests for readability
- Converted several single-line guard-style `if let`/`let Some` exits in `fixup.rs` into multiline blocks for consistent style. - Reformatted the safe-argument regex definition and normalized test case formatting in `fixup.rs` without altering assertions. - Reordered `lib.rs` module exports by moving `fixup` ahead of `minimizer`.
This commit is contained in:
@@ -8,10 +8,10 @@
|
|||||||
//! truncates bash output and exposes the full result via an artifact, so
|
//! truncates bash output and exposes the full result via an artifact, so
|
||||||
//! the pipe just hides content the agent wanted.
|
//! the pipe just hides content the agent wanted.
|
||||||
//!
|
//!
|
||||||
//! 2. A redundant trailing `2>&1` on a segment that has no remaining pipe
|
//! 2. A redundant trailing `2>&1` on a segment that has no remaining pipe or
|
||||||
//! or other redirect. The harness already merges stderr into stdout, so
|
//! other redirect. The harness already merges stderr into stdout, so the
|
||||||
//! the duplication is purely cosmetic — and often a leftover after fixup
|
//! duplication is purely cosmetic — and often a leftover after fixup (1)
|
||||||
//! (1) drops a downstream pipe.
|
//! drops a downstream pipe.
|
||||||
//!
|
//!
|
||||||
//! The implementation is AST-driven: `brush-parser` handles tokenization,
|
//! The implementation is AST-driven: `brush-parser` handles tokenization,
|
||||||
//! quoting, heredocs, command substitution, and nested compound commands. We
|
//! quoting, heredocs, command substitution, and nested compound commands. We
|
||||||
@@ -142,7 +142,9 @@ fn try_strip_head_tail(
|
|||||||
if !is_safe_head_tail(last) {
|
if !is_safe_head_tail(last) {
|
||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
let Some(last_loc) = last.location() else { return default };
|
let Some(last_loc) = last.location() else {
|
||||||
|
return default;
|
||||||
|
};
|
||||||
|
|
||||||
// Pipeline-internal separators are always `|` or `|&` — never `||`. The
|
// Pipeline-internal separators are always `|` or `|&` — never `||`. The
|
||||||
// real parser already validated structure, so scanning backwards from the
|
// real parser already validated structure, so scanning backwards from the
|
||||||
@@ -153,8 +155,12 @@ fn try_strip_head_tail(
|
|||||||
// (e.g. the synthetic `2>&1` inserted by `|&`).
|
// (e.g. the synthetic `2>&1` inserted by `|&`).
|
||||||
let bytes = cmd.as_bytes();
|
let bytes = cmd.as_bytes();
|
||||||
let last_start = last_loc.start.index;
|
let last_start = last_loc.start.index;
|
||||||
let Some(head) = cmd.get(..last_start) else { return default };
|
let Some(head) = cmd.get(..last_start) else {
|
||||||
let Some(pipe_pos) = head.rfind('|') else { return default };
|
return default;
|
||||||
|
};
|
||||||
|
let Some(pipe_pos) = head.rfind('|') else {
|
||||||
|
return default;
|
||||||
|
};
|
||||||
// Defense in depth against `||`.
|
// Defense in depth against `||`.
|
||||||
if pipe_pos > 0 && bytes[pipe_pos - 1] == b'|' {
|
if pipe_pos > 0 && bytes[pipe_pos - 1] == b'|' {
|
||||||
return default;
|
return default;
|
||||||
@@ -198,8 +204,12 @@ fn try_strip_2to1(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let target = &p.seq[outcome.last_idx];
|
let target = &p.seq[outcome.last_idx];
|
||||||
let Command::Simple(simple) = target else { return };
|
let Command::Simple(simple) = target else {
|
||||||
let Some(name_word) = simple.word_or_name.as_ref() else { return };
|
return;
|
||||||
|
};
|
||||||
|
let Some(name_word) = simple.word_or_name.as_ref() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
if name_word.value.is_empty() {
|
if name_word.value.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -210,8 +220,12 @@ fn try_strip_2to1(
|
|||||||
|
|
||||||
// The last suffix item must be the `2>&1` redirect, and it must be the
|
// The last suffix item must be the `2>&1` redirect, and it must be the
|
||||||
// only redirect on the command (no `> file 2>&1` or `2>&1 > file`).
|
// only redirect on the command (no `> file 2>&1` or `2>&1 > file`).
|
||||||
let Some(last_item) = suffix.0.last() else { return };
|
let Some(last_item) = suffix.0.last() else {
|
||||||
let CommandPrefixOrSuffixItem::IoRedirect(io) = last_item else { return };
|
return;
|
||||||
|
};
|
||||||
|
let CommandPrefixOrSuffixItem::IoRedirect(io) = last_item else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
if !is_stderr_to_stdout(io) {
|
if !is_stderr_to_stdout(io) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -236,7 +250,9 @@ fn try_strip_2to1(
|
|||||||
// accounted for by the AST; the gap between the anchor and `2>&1` is
|
// accounted for by the AST; the gap between the anchor and `2>&1` is
|
||||||
// guaranteed to be just whitespace by the precondition that `2>&1` is
|
// guaranteed to be just whitespace by the precondition that `2>&1` is
|
||||||
// the last suffix item and no other redirects exist.
|
// the last suffix item and no other redirects exist.
|
||||||
let Some(name_loc) = name_word.loc.as_ref() else { return };
|
let Some(name_loc) = name_word.loc.as_ref() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let mut anchor = name_loc.end.index;
|
let mut anchor = name_loc.end.index;
|
||||||
for item in &suffix.0 {
|
for item in &suffix.0 {
|
||||||
if let Some(loc) = item.location() {
|
if let Some(loc) = item.location() {
|
||||||
@@ -279,8 +295,12 @@ fn is_stderr_to_stdout(io: &IoRedirect) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn is_safe_head_tail(c: &Command) -> bool {
|
fn is_safe_head_tail(c: &Command) -> bool {
|
||||||
let Command::Simple(simple) = c else { return false };
|
let Command::Simple(simple) = c else {
|
||||||
let Some(name) = simple.word_or_name.as_ref() else { return false };
|
return false;
|
||||||
|
};
|
||||||
|
let Some(name) = simple.word_or_name.as_ref() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
if name.value != "head" && name.value != "tail" {
|
if name.value != "head" && name.value != "tail" {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -291,9 +311,13 @@ fn is_safe_head_tail(c: &Command) -> bool {
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let Some(suffix) = &simple.suffix else { return true };
|
let Some(suffix) = &simple.suffix else {
|
||||||
|
return true;
|
||||||
|
};
|
||||||
for item in &suffix.0 {
|
for item in &suffix.0 {
|
||||||
let CommandPrefixOrSuffixItem::Word(w) = item else { return false };
|
let CommandPrefixOrSuffixItem::Word(w) = item else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
if !SAFE_ARG_RE.is_match(&w.value) {
|
if !SAFE_ARG_RE.is_match(&w.value) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -314,8 +338,10 @@ fn is_safe_head_tail(c: &Command) -> bool {
|
|||||||
/// `--help`, and any filename token are deliberately rejected — they would
|
/// `--help`, and any filename token are deliberately rejected — they would
|
||||||
/// change semantics if their host command were removed.
|
/// change semantics if their host command were removed.
|
||||||
static SAFE_ARG_RE: LazyLock<Regex> = LazyLock::new(|| {
|
static SAFE_ARG_RE: LazyLock<Regex> = LazyLock::new(|| {
|
||||||
Regex::new(r"^(?:-[nc]=?\d+|-[nc]|-\d+|-[qv]|--lines(?:=\d+)?|--bytes(?:=\d+)?|--quiet|--verbose|\d+)$")
|
Regex::new(
|
||||||
.expect("static safe-arg regex compiles")
|
r"^(?:-[nc]=?\d+|-[nc]|-\d+|-[qv]|--lines(?:=\d+)?|--bytes(?:=\d+)?|--quiet|--verbose|\d+)$",
|
||||||
|
)
|
||||||
|
.expect("static safe-arg regex compiles")
|
||||||
});
|
});
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -359,16 +385,8 @@ mod tests {
|
|||||||
let cases: &[(&str, &str, &[&str])] = &[
|
let cases: &[(&str, &str, &[&str])] = &[
|
||||||
("cmd 2>&1", "cmd", &["2>&1"]),
|
("cmd 2>&1", "cmd", &["2>&1"]),
|
||||||
("just build 2>&1", "just build", &["2>&1"]),
|
("just build 2>&1", "just build", &["2>&1"]),
|
||||||
(
|
("just build 2>&1 | tail -3", "just build", &["| tail -3", "2>&1"]),
|
||||||
"just build 2>&1 | tail -3",
|
("cargo build 2>&1 | head -50", "cargo build", &["| head -50", "2>&1"]),
|
||||||
"just build",
|
|
||||||
&["| tail -3", "2>&1"],
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"cargo build 2>&1 | head -50",
|
|
||||||
"cargo build",
|
|
||||||
&["| head -50", "2>&1"],
|
|
||||||
),
|
|
||||||
];
|
];
|
||||||
for (input, want_cmd, want_stripped) in cases {
|
for (input, want_cmd, want_stripped) in cases {
|
||||||
let (cmd, stripped) = run(input);
|
let (cmd, stripped) = run(input);
|
||||||
@@ -385,26 +403,13 @@ mod tests {
|
|||||||
"just build && just up && sleep 4 && just healthz",
|
"just build && just up && sleep 4 && just healthz",
|
||||||
&["| tail -3", "2>&1"],
|
&["| tail -3", "2>&1"],
|
||||||
),
|
),
|
||||||
(
|
("cmd1 | head -5 && cmd2 && cmd3 | tail -3", "cmd1 && cmd2 && cmd3", &[
|
||||||
"cmd1 | head -5 && cmd2 && cmd3 | tail -3",
|
"| head -5",
|
||||||
"cmd1 && cmd2 && cmd3",
|
"| tail -3",
|
||||||
&["| head -5", "| tail -3"],
|
]),
|
||||||
),
|
("echo a; cmd | head -5; echo b", "echo a; cmd; echo b", &["| head -5"]),
|
||||||
(
|
("cmd | head -5 || fallback | tail -3", "cmd || fallback", &["| head -5", "| tail -3"]),
|
||||||
"echo a; cmd | head -5; echo b",
|
("cmd1 | head -5 && cmd2 2>&1 | grep err", "cmd1 && cmd2 2>&1 | grep err", &["| head -5"]),
|
||||||
"echo a; cmd; echo b",
|
|
||||||
&["| head -5"],
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"cmd | head -5 || fallback | tail -3",
|
|
||||||
"cmd || fallback",
|
|
||||||
&["| head -5", "| tail -3"],
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"cmd1 | head -5 && cmd2 2>&1 | grep err",
|
|
||||||
"cmd1 && cmd2 2>&1 | grep err",
|
|
||||||
&["| head -5"],
|
|
||||||
),
|
|
||||||
];
|
];
|
||||||
for (input, want_cmd, want_stripped) in cases {
|
for (input, want_cmd, want_stripped) in cases {
|
||||||
let (cmd, stripped) = run(input);
|
let (cmd, stripped) = run(input);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
pub mod cancel;
|
pub mod cancel;
|
||||||
pub mod minimizer;
|
|
||||||
pub mod fixup;
|
pub mod fixup;
|
||||||
|
pub mod minimizer;
|
||||||
pub mod process;
|
pub mod process;
|
||||||
pub mod shell;
|
pub mod shell;
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
|
|||||||
Reference in New Issue
Block a user