From cd9e19e1bc863c25e6eaf3c1dcb0399b80ce09c2 Mon Sep 17 00:00:00 2001 From: Mykhailo Chalyi Date: Sat, 28 Mar 2026 10:35:36 +0000 Subject: [PATCH] refactor(builtins): migrate base64, date, cut to ArgParser Replace manual `while i < ctx.args.len()` loops with ArgParser in base64, date, and cut builtins. This is the first batch of the incremental migration described in #880. Ref #880 --- crates/bashkit/src/builtins/base64.rs | 49 +++++++++++--------- crates/bashkit/src/builtins/cuttr.rs | 67 +++++++++++---------------- crates/bashkit/src/builtins/date.rs | 41 +++++++++------- 3 files changed, 76 insertions(+), 81 deletions(-) diff --git a/crates/bashkit/src/builtins/base64.rs b/crates/bashkit/src/builtins/base64.rs index 1590eed8..964e0c17 100644 --- a/crates/bashkit/src/builtins/base64.rs +++ b/crates/bashkit/src/builtins/base64.rs @@ -23,35 +23,38 @@ impl Builtin for Base64 { let mut wrap = 76usize; let mut file: Option = None; - let mut i = 0; - while i < ctx.args.len() { - match ctx.args[i].as_str() { - "-d" | "--decode" => decode = true, - "-w" => { - i += 1; - if i >= ctx.args.len() { + let mut p = super::arg_parser::ArgParser::new(ctx.args); + while !p.is_done() { + if p.flag_any(&["-d", "--decode"]) { + decode = true; + } else if let Some(val) = p.current().and_then(|s| s.strip_prefix("--wrap=")) { + wrap = val.parse().unwrap_or(76); + p.advance(); + } else { + match p.flag_value("-w", "base64") { + Ok(Some(val)) => wrap = val.parse().unwrap_or(76), + Err(_) => { return Ok(ExecResult::err( - "base64: option requires an argument -- 'w'\n".to_string(), + "base64: option requires an argument -- 'w'\n", 1, )); } - wrap = ctx.args[i].parse().unwrap_or(76); - } - s if s.starts_with("--wrap=") => { - wrap = s[7..].parse().unwrap_or(76); - } - "-i" | "--ignore-garbage" => { /* silently accept */ } - s if s.starts_with('-') && s != "-" => { - return Ok(ExecResult::err( - format!("base64: invalid option -- '{}'\n", &s[1..]), - 1, - )); - } - _ => { - file = Some(ctx.args[i].clone()); + Ok(None) => { + if p.flag_any(&["-i", "--ignore-garbage"]) { + // silently accept + } else if let Some(flag) = + p.current().filter(|s| s.starts_with('-') && s.len() > 1) + { + return Ok(ExecResult::err( + format!("base64: invalid option -- '{}'\n", &flag[1..]), + 1, + )); + } else if let Some(arg) = p.positional() { + file = Some(arg.to_string()); + } + } } } - i += 1; } // Get input: from file, stdin, or empty diff --git a/crates/bashkit/src/builtins/cuttr.rs b/crates/bashkit/src/builtins/cuttr.rs index 9cbe1757..2e386374 100644 --- a/crates/bashkit/src/builtins/cuttr.rs +++ b/crates/bashkit/src/builtins/cuttr.rs @@ -41,54 +41,41 @@ impl Builtin for Cut { let mut files = Vec::new(); // Parse arguments - let mut i = 0; - while i < ctx.args.len() { - let arg = &ctx.args[i]; - if arg == "-d" { - i += 1; - if i < ctx.args.len() { - delimiter = ctx.args[i].chars().next().unwrap_or('\t'); - } - } else if let Some(d) = arg.strip_prefix("-d") { - delimiter = d.chars().next().unwrap_or('\t'); - } else if arg == "-f" { - i += 1; - if i < ctx.args.len() { - spec = ctx.args[i].clone(); - mode = CutMode::Fields; - } - } else if let Some(f) = arg.strip_prefix("-f") { - spec = f.to_string(); + let mut p = super::arg_parser::ArgParser::new(ctx.args); + while !p.is_done() { + if let Some(val) = p.flag_value_opt("-d") { + delimiter = val.chars().next().unwrap_or('\t'); + } else if let Some(val) = p.flag_value_opt("-f") { + spec = val.to_string(); mode = CutMode::Fields; - } else if arg == "-c" || arg == "-b" { - i += 1; - if i < ctx.args.len() { - spec = ctx.args[i].clone(); - mode = CutMode::Chars; - } - } else if let Some(c) = arg.strip_prefix("-c") { - spec = c.to_string(); + } else if let Some(val) = p.flag_value_opt("-c") { + spec = val.to_string(); mode = CutMode::Chars; - } else if let Some(b) = arg.strip_prefix("-b") { - spec = b.to_string(); + } else if let Some(val) = p.flag_value_opt("-b") { + spec = val.to_string(); mode = CutMode::Chars; - } else if arg == "-s" { + } else if p.flag("-s") { only_delimited = true; - } else if arg == "-z" { + } else if p.flag("-z") { zero_terminated = true; - } else if arg == "--complement" { + } else if p.flag("--complement") { complement = true; - } else if let Some(od) = arg.strip_prefix("--output-delimiter=") { - output_delimiter = Some(od.to_string()); - } else if arg == "--output-delimiter" { - i += 1; - if i < ctx.args.len() { - output_delimiter = Some(ctx.args[i].clone()); + } else if let Some(val) = p + .current() + .and_then(|s| s.strip_prefix("--output-delimiter=")) + { + output_delimiter = Some(val.to_string()); + p.advance(); + } else if p.flag("--output-delimiter") { + if let Some(val) = p.positional() { + output_delimiter = Some(val.to_string()); } - } else if !arg.starts_with('-') { - files.push(arg.clone()); + } else if let Some(arg) = p.current().filter(|s| !s.starts_with('-')) { + files.push(arg.to_string()); + p.advance(); + } else { + p.advance(); } - i += 1; } if spec.is_empty() { diff --git a/crates/bashkit/src/builtins/date.rs b/crates/bashkit/src/builtins/date.rs index 3dade976..9996c9d5 100644 --- a/crates/bashkit/src/builtins/date.rs +++ b/crates/bashkit/src/builtins/date.rs @@ -330,30 +330,35 @@ impl Builtin for Date { let mut rfc2822 = false; let mut iso8601: Option = None; - let mut i = 0; - while i < ctx.args.len() { - let arg = &ctx.args[i]; - if arg == "-u" || arg == "--utc" { + let mut p = super::arg_parser::ArgParser::new(ctx.args); + while !p.is_done() { + if p.flag_any(&["-u", "--utc"]) { utc = true; - } else if arg == "-d" || arg == "--date" { - i += 1; - if i < ctx.args.len() { - date_str = Some(ctx.args[i].clone()); - } - } else if let Some(val) = arg.strip_prefix("--date=") { + } else if let Some(val) = p.current().and_then(|s| s.strip_prefix("--date=")) { date_str = Some(strip_surrounding_quotes(val).to_string()); - } else if arg == "-R" || arg == "--rfc-2822" || arg == "--rfc-email" { + p.advance(); + } else if let Some(val) = p.flag_value_opt("-d") { + date_str = Some(val.to_string()); + } else if p.flag("--date") { + if let Some(val) = p.positional() { + date_str = Some(val.to_string()); + } + } else if p.flag_any(&["-R", "--rfc-2822", "--rfc-email"]) { rfc2822 = true; - } else if arg == "-I" || arg == "--iso-8601" { - iso8601 = Some("date".to_string()); - } else if let Some(val) = arg.strip_prefix("-I") { + } else if let Some(val) = p.current().and_then(|s| s.strip_prefix("--iso-8601=")) { iso8601 = Some(val.to_string()); - } else if let Some(val) = arg.strip_prefix("--iso-8601=") { + p.advance(); + } else if p.flag_any(&["-I", "--iso-8601"]) { + iso8601 = Some("date".to_string()); + } else if let Some(val) = p.current().and_then(|s| s.strip_prefix("-I")) { iso8601 = Some(val.to_string()); - } else if arg.starts_with('+') { - format_arg = Some(arg.clone()); + p.advance(); + } else if let Some(arg) = p.current().filter(|s| s.starts_with('+')) { + format_arg = Some(arg.to_string()); + p.advance(); + } else { + p.advance(); } - i += 1; } // Get the datetime to format