Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions crates/bashkit/src/builtins/awk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -682,6 +682,14 @@ impl<'a> AwkParser<'a> {
fn parse_printf(&mut self) -> Result<AwkAction> {
self.skip_whitespace();

// Handle optional parenthesized form: printf("format", args)
let has_parens =
self.pos < self.input.len() && self.input.chars().nth(self.pos).unwrap() == '(';
if has_parens {
self.pos += 1;
self.skip_whitespace();
}

// Parse format string
if self.pos >= self.input.len() || self.input.chars().nth(self.pos).unwrap() != '"' {
return Err(Error::Execution(
Expand All @@ -701,6 +709,13 @@ impl<'a> AwkParser<'a> {
self.skip_whitespace();
}

if has_parens
&& self.pos < self.input.len()
&& self.input.chars().nth(self.pos).unwrap() == ')'
{
self.pos += 1;
}

Ok(AwkAction::Printf(format, args))
}

Expand Down Expand Up @@ -3269,4 +3284,32 @@ mod tests {
.unwrap();
assert_eq!(result.stdout, "329\n");
}

#[tokio::test]
async fn test_awk_printf_parens() {
// printf with parenthesized syntax: printf("format", args)
let result = run_awk(
&[r#"BEGIN{printf("["); printf("%s", "x"); printf("]"); print ""}"#],
Some(""),
)
.await
.unwrap();
assert_eq!(result.stdout, "[x]\n");
}

#[tokio::test]
async fn test_awk_printf_parens_csv() {
// CSV to JSON pattern using printf with parens
let result = run_awk(
&[
"-F,",
r#"NR==1{for(i=1;i<=NF;i++) h[i]=$i; next} {printf("%s{", (NR>2?",":"")); for(i=1;i<=NF;i++){printf("%s\"%s\":\"%s\"", (i>1?",":""), h[i], $i)}; printf("}")} END{print ""}"#,
],
Some("name,age\nalice,30\nbob,25\n"),
)
.await
.unwrap();
assert!(result.stdout.contains("alice"));
assert!(result.stdout.contains("bob"));
}
}
44 changes: 44 additions & 0 deletions crates/bashkit/src/builtins/jq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ impl Builtin for Jq {
// Parse arguments for flags using index-based loop to support
// multi-arg flags like --arg name value and --argjson name value.
let mut raw_output = false;
let mut raw_input = false;
let mut compact_output = false;
let mut null_input = false;
let mut sort_keys = false;
Expand Down Expand Up @@ -210,6 +211,8 @@ impl Builtin for Jq {

if arg == "--raw-output" {
raw_output = true;
} else if arg == "--raw-input" {
raw_input = true;
} else if arg == "--compact-output" {
compact_output = true;
} else if arg == "--null-input" {
Expand Down Expand Up @@ -269,6 +272,7 @@ impl Builtin for Jq {
for ch in arg[1..].chars() {
match ch {
'r' => raw_output = true,
'R' => raw_input = true,
'c' => compact_output = true,
'n' => null_input = true,
'S' => sort_keys = true,
Expand Down Expand Up @@ -377,6 +381,15 @@ impl Builtin for Jq {
let inputs_to_process: Vec<Val> = if null_input {
// -n flag: use null as input
vec![Val::from(serde_json::Value::Null)]
} else if raw_input && slurp {
// -Rs flag: read entire input as single string
vec![Val::from(serde_json::Value::String(input.to_string()))]
} else if raw_input {
// -R flag: each line becomes a JSON string value
input
.lines()
.map(|line| Val::from(serde_json::Value::String(line.to_string())))
.collect()
} else if slurp {
// -s flag: read all inputs into a single array
match Self::parse_json_values(input) {
Expand Down Expand Up @@ -1189,4 +1202,35 @@ mod tests {
let result = run_jq_with_args(&["-snr", r#""hello""#], "").await.unwrap();
assert_eq!(result.trim(), "hello");
}

#[tokio::test]
async fn test_jq_raw_input() {
// -R: each line becomes a JSON string
let result = run_jq_with_args(&["-R", "."], "hello\nworld\n")
.await
.unwrap();
assert_eq!(result.trim(), "\"hello\"\n\"world\"");
}

#[tokio::test]
async fn test_jq_raw_input_slurp() {
// -Rs: entire input as one string
let result = run_jq_with_args(&["-Rs", "."], "hello\nworld\n")
.await
.unwrap();
assert_eq!(result.trim(), "\"hello\\nworld\\n\"");
}

#[tokio::test]
async fn test_jq_raw_input_split() {
// -R -s then split: CSV-like processing
let result = run_jq_with_args(
&["-Rs", r#"split("\n") | map(select(length>0))"#],
"a,b,c\n1,2,3\n",
)
.await
.unwrap();
assert!(result.contains("a,b,c"));
assert!(result.contains("1,2,3"));
}
}
14 changes: 14 additions & 0 deletions crates/bashkit/tests/spec_cases/awk/awk.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -650,3 +650,17 @@ printf '10\n2\n' | awk '{if ($1 > 5) print $1}'
### expect
10
### end

### awk_printf_parens
# printf with parenthesized form
printf 'x\n' | awk '{printf("[%s]", $1); print ""}'
### expect
[x]
### end

### awk_printf_parens_begin
# printf with parens in BEGIN block
echo x | awk 'BEGIN{printf("["); printf("%s", "hi"); printf("]"); print ""}'
### expect
[hi]
### end
18 changes: 18 additions & 0 deletions crates/bashkit/tests/spec_cases/jq/jq.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -954,3 +954,21 @@ echo '42' | jq -e '.'
### expect
42
### end

### jq_raw_input
# -R flag: each line treated as string
printf 'hello\nworld\n' | jq -R '.'
### expect
"hello"
"world"
### end

### jq_raw_input_slurp
# -Rs flag: entire input as one string, then split
printf 'a,b\n1,2\n' | jq -Rs 'split("\n") | map(select(length>0))'
### expect
[
"a,b",
"1,2"
]
### end
Loading