Skip to content

Commit dd3dab7

Browse files
cfsmp3claude
andcommitted
fix(args): Add backward compatibility for single-dash long options (CCExtractor#1576)
Old versions of ccextractor accepted single-dash long options like -quiet, -stdout, -autoprogram. The new Rust-based argument parser (clap) only accepts double-dash options (--quiet, --stdout, etc.). When users ran scripts with -quiet, clap parsed it as individual short options -q -u -i -e -t and failed with exit code 7. Users with stderr redirected never saw the error, causing silent failures with zero-length output files. This adds a normalize_legacy_option() function that pre-processes arguments before passing them to clap: - Single-dash long options (e.g., -quiet) convert to --quiet - Double-dash options remain unchanged - Short options like -o remain unchanged - Numeric options like -1, -12 remain unchanged Includes 6 unit tests for the new function. Fixes CCExtractor#1576 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent c3f637a commit dd3dab7

File tree

1 file changed

+138
-0
lines changed

1 file changed

+138
-0
lines changed

src/rust/src/lib.rs

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,35 @@ extern "C" fn ccxr_close_handle(handle: RawHandle) {
358358
}
359359
}
360360

361+
/// Normalize legacy single-dash long options to double-dash format.
362+
///
363+
/// Old versions of ccextractor accepted `-quiet`, `-stdout`, etc. but clap
364+
/// requires `--quiet`, `--stdout`. This function converts single-dash long
365+
/// options to double-dash for backward compatibility.
366+
///
367+
/// # Rules
368+
/// - Single-dash options with multiple characters (e.g., `-quiet`) are converted to `--quiet`
369+
/// - Double-dash options (e.g., `--quiet`) are left unchanged
370+
/// - Single-letter short options (e.g., `-o`) are left unchanged
371+
/// - Non-option arguments (e.g., `file.ts`) are left unchanged
372+
/// - Numeric options (e.g., `-1`, `-12`) are left unchanged (these are valid short options)
373+
fn normalize_legacy_option(arg: String) -> String {
374+
// Check if it's a single-dash option with multiple characters (e.g., -quiet)
375+
// but not a short option with a value (e.g., -o filename)
376+
// Single-letter options like -o, -s should be left unchanged
377+
// Numeric options like -1, -12 should also be left unchanged
378+
if arg.starts_with('-')
379+
&& !arg.starts_with("--")
380+
&& arg.len() > 2
381+
&& arg.chars().nth(1).is_some_and(|c| c.is_ascii_alphabetic())
382+
{
383+
// Convert -option to --option
384+
format!("-{}", arg)
385+
} else {
386+
arg
387+
}
388+
}
389+
361390
/// # Safety
362391
/// Safe if argv is a valid pointer
363392
///
@@ -380,6 +409,11 @@ pub unsafe extern "C" fn ccxr_parse_parameters(argc: c_int, argv: *mut *mut c_ch
380409
return ExitCause::NoInputFiles.exit_code();
381410
}
382411

412+
// Backward compatibility: Convert single-dash long options to double-dash
413+
// Old versions of ccextractor accepted -quiet, -stdout, etc. but clap requires --quiet, --stdout
414+
// This allows scripts using the old syntax to continue working
415+
let args: Vec<String> = args.into_iter().map(normalize_legacy_option).collect();
416+
383417
let args: Args = match Args::try_parse_from(args) {
384418
Ok(args) => args,
385419
Err(e) => {
@@ -482,4 +516,108 @@ mod test {
482516
assert_eq!(decoder_ctx.processed_enough, 0);
483517
assert_eq!(unsafe { cb_708 }, 11);
484518
}
519+
520+
#[test]
521+
fn test_normalize_legacy_option_single_dash_long() {
522+
// Single-dash long options should be converted to double-dash
523+
assert_eq!(
524+
normalize_legacy_option("-quiet".to_string()),
525+
"--quiet".to_string()
526+
);
527+
assert_eq!(
528+
normalize_legacy_option("-stdout".to_string()),
529+
"--stdout".to_string()
530+
);
531+
assert_eq!(
532+
normalize_legacy_option("-autoprogram".to_string()),
533+
"--autoprogram".to_string()
534+
);
535+
assert_eq!(
536+
normalize_legacy_option("-goptime".to_string()),
537+
"--goptime".to_string()
538+
);
539+
}
540+
541+
#[test]
542+
fn test_normalize_legacy_option_double_dash() {
543+
// Double-dash options should remain unchanged
544+
assert_eq!(
545+
normalize_legacy_option("--quiet".to_string()),
546+
"--quiet".to_string()
547+
);
548+
assert_eq!(
549+
normalize_legacy_option("--stdout".to_string()),
550+
"--stdout".to_string()
551+
);
552+
assert_eq!(
553+
normalize_legacy_option("--autoprogram".to_string()),
554+
"--autoprogram".to_string()
555+
);
556+
}
557+
558+
#[test]
559+
fn test_normalize_legacy_option_short_options() {
560+
// Single-letter short options should remain unchanged
561+
assert_eq!(
562+
normalize_legacy_option("-o".to_string()),
563+
"-o".to_string()
564+
);
565+
assert_eq!(
566+
normalize_legacy_option("-s".to_string()),
567+
"-s".to_string()
568+
);
569+
}
570+
571+
#[test]
572+
fn test_normalize_legacy_option_numeric_options() {
573+
// Numeric options should remain unchanged (these are valid ccextractor options)
574+
assert_eq!(
575+
normalize_legacy_option("-1".to_string()),
576+
"-1".to_string()
577+
);
578+
assert_eq!(
579+
normalize_legacy_option("-2".to_string()),
580+
"-2".to_string()
581+
);
582+
assert_eq!(
583+
normalize_legacy_option("-12".to_string()),
584+
"-12".to_string()
585+
);
586+
}
587+
588+
#[test]
589+
fn test_normalize_legacy_option_non_options() {
590+
// Non-option arguments should remain unchanged
591+
assert_eq!(
592+
normalize_legacy_option("file.ts".to_string()),
593+
"file.ts".to_string()
594+
);
595+
assert_eq!(
596+
normalize_legacy_option("/path/to/file.ts".to_string()),
597+
"/path/to/file.ts".to_string()
598+
);
599+
assert_eq!(
600+
normalize_legacy_option("ccextractor".to_string()),
601+
"ccextractor".to_string()
602+
);
603+
}
604+
605+
#[test]
606+
fn test_normalize_legacy_option_edge_cases() {
607+
// Empty string
608+
assert_eq!(
609+
normalize_legacy_option("".to_string()),
610+
"".to_string()
611+
);
612+
// Just a dash
613+
assert_eq!(
614+
normalize_legacy_option("-".to_string()),
615+
"-".to_string()
616+
);
617+
// Double dash alone (end of options marker)
618+
assert_eq!(
619+
normalize_legacy_option("--".to_string()),
620+
"--".to_string()
621+
);
622+
}
485623
}

0 commit comments

Comments
 (0)