Coverage Report

Created: 2025-09-08 21:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/home/noah/src/ruchy/src/bin/handlers/mod.rs
Line
Count
Source
1
use anyhow::{Context, Result};
2
3
mod commands;
4
mod handlers_modules;
5
use ruchy::{Parser as RuchyParser, Transpiler};
6
use ruchy::frontend::ast::Expr;
7
use ruchy::runtime::Repl;
8
use ruchy::runtime::replay_converter::ConversionConfig;
9
// Replay functionality imports removed - not needed in handler, used directly in REPL
10
use std::fs;
11
use std::io::{self, Read};
12
use std::path::{Path, PathBuf};
13
14
/// Handle eval command - evaluate a one-liner expression with -e flag
15
/// 
16
/// # Arguments
17
/// * `expr` - The expression to evaluate
18
/// * `verbose` - Enable verbose output
19
/// * `format` - Output format ("json" or default text)
20
/// 
21
/// # Examples
22
/// ```
23
/// // This function is typically called by the CLI with parsed arguments
24
/// // handle_eval_command("2 + 2", false, "text");
25
/// ```
26
/// 
27
/// # Errors
28
/// Returns error if expression cannot be parsed or evaluated
29
0
pub fn handle_eval_command(expr: &str, verbose: bool, format: &str) -> Result<()> {
30
0
    if verbose {
31
0
        eprintln!("Parsing expression: {expr}");
32
0
    }
33
34
0
    let mut repl = Repl::new()?;
35
0
    match repl.eval(expr) {
36
0
        Ok(result) => {
37
0
            if verbose {
38
0
                eprintln!("Evaluation successful");
39
0
            }
40
41
0
            if format == "json" {
42
0
                // Manually construct JSON to ensure field order matches test expectations
43
0
                let result_str = result.replace('"', "\\\"");
44
0
                println!("{{\"success\":true,\"result\":\"{result_str}\"}}");
45
0
            } else {
46
0
                // Default text output - always show result for one-liner evaluation
47
0
                println!("{result}");
48
0
            }
49
0
            Ok(())
50
        }
51
0
        Err(e) => {
52
0
            if verbose {
53
0
                eprintln!("Evaluation failed: {e}");
54
0
            }
55
56
0
            match format {
57
0
                "json" => {
58
0
                    println!(
59
0
                        "{}",
60
0
                        serde_json::json!({
61
0
                            "success": false,
62
0
                            "error": e.to_string()
63
0
                        })
64
0
                    );
65
0
                }
66
0
                _ => {
67
0
                    eprintln!("Error: {e}");
68
0
                }
69
            }
70
0
            std::process::exit(1);
71
        }
72
    }
73
0
}
74
75
/// Handle file execution - run a Ruchy script file directly (not via subcommand)
76
/// 
77
/// # Arguments
78
/// * `file` - Path to the Ruchy file to execute
79
/// 
80
/// # Examples
81
/// ```
82
/// // This function is typically called by the CLI
83
/// // handle_file_execution(&Path::new("script.ruchy"));
84
/// ```
85
/// 
86
/// # Errors
87
/// Returns error if file cannot be read, parsed, or executed
88
0
pub fn handle_file_execution(file: &Path) -> Result<()> {
89
0
    let source = fs::read_to_string(file)
90
0
        .with_context(|| format!("Failed to read file: {}", file.display()))?;
91
92
    // Use REPL to evaluate the file
93
0
    let mut repl = Repl::new()?;
94
0
    match repl.eval(&source) {
95
0
        Ok(result) => {
96
            // Only print non-unit results
97
0
            if result != "Unit" && result != "()" {
98
0
                println!("{result}");
99
0
            }
100
0
            Ok(())
101
        }
102
0
        Err(e) => {
103
0
            eprintln!("Error: {e}");
104
0
            std::process::exit(1);
105
        }
106
    }
107
0
}
108
109
/// Handle stdin/piped input - evaluate input from standard input
110
/// 
111
/// # Arguments
112
/// * `input` - The input string to evaluate
113
/// 
114
/// # Examples
115
/// ```
116
/// // This function is typically called when input is piped to the CLI
117
/// // handle_stdin_input("2 + 2");
118
/// ```
119
/// 
120
/// # Errors
121
/// Returns error if input cannot be parsed or evaluated
122
0
pub fn handle_stdin_input(input: &str) -> Result<()> {
123
0
    let mut repl = Repl::new()?;
124
0
    match repl.eval(input) {
125
0
        Ok(result) => {
126
0
            println!("{result}");
127
0
            Ok(())
128
        }
129
0
        Err(e) => {
130
0
            eprintln!("Error: {e}");
131
0
            std::process::exit(1);
132
        }
133
    }
134
0
}
135
136
/// Handle parse command - show AST for a Ruchy file
137
0
pub fn handle_parse_command(file: &Path, verbose: bool) -> Result<()> {
138
0
    if verbose {
139
0
        eprintln!("Parsing file: {}", file.display());
140
0
    }
141
    
142
0
    let source = fs::read_to_string(file)
143
0
        .with_context(|| format!("Failed to read file: {}", file.display()))?;
144
    
145
0
    let mut parser = RuchyParser::new(&source);
146
0
    match parser.parse() {
147
0
        Ok(ast) => {
148
0
            println!("{ast:#?}");
149
0
            Ok(())
150
        }
151
0
        Err(e) => {
152
0
            eprintln!("Parse error: {e}");
153
0
            std::process::exit(1);
154
        }
155
    }
156
0
}
157
158
/// Handle transpile command - convert Ruchy to Rust
159
0
pub fn handle_transpile_command(
160
0
    file: &Path, 
161
0
    output: Option<&Path>, 
162
0
    minimal: bool,
163
0
    verbose: bool
164
0
) -> Result<()> {
165
0
    log_transpile_start(file, minimal, verbose);
166
    
167
0
    let source = read_source_file(file, verbose)?;
168
0
    let ast = parse_source(&source)?;
169
0
    let rust_code = transpile_ast(&ast, minimal)?;
170
    
171
0
    write_output(&rust_code, output, verbose)?;
172
0
    Ok(())
173
0
}
174
175
/// Log transpilation start (complexity: 3)
176
0
fn log_transpile_start(file: &Path, minimal: bool, verbose: bool) {
177
0
    if !verbose {
178
0
        return;
179
0
    }
180
0
    eprintln!("Transpiling file: {}", file.display());
181
0
    if minimal {
182
0
        eprintln!("Using minimal codegen for self-hosting");
183
0
    }
184
0
}
185
186
/// Read source from file or stdin (complexity: 5)
187
0
fn read_source_file(file: &Path, verbose: bool) -> Result<String> {
188
0
    if file.as_os_str() == "-" {
189
0
        if verbose {
190
0
            eprintln!("Reading from stdin...");
191
0
        }
192
0
        let mut input = String::new();
193
0
        io::stdin().read_to_string(&mut input)?;
194
0
        Ok(input)
195
    } else {
196
0
        fs::read_to_string(file)
197
0
            .with_context(|| format!("Failed to read file: {}", file.display()))
198
    }
199
0
}
200
201
/// Parse source code to AST (complexity: 2)
202
0
fn parse_source(source: &str) -> Result<Expr> {
203
0
    let mut parser = RuchyParser::new(source);
204
0
    parser.parse()
205
0
        .with_context(|| "Failed to parse input")
206
0
}
207
208
/// Transpile AST to Rust code (complexity: 4)
209
0
fn transpile_ast(ast: &Expr, minimal: bool) -> Result<String> {
210
0
    let mut transpiler = Transpiler::new();
211
0
    if minimal {
212
0
        transpiler.transpile_minimal(ast)
213
0
            .with_context(|| "Failed to transpile to Rust (minimal)")
214
    } else {
215
0
        transpiler.transpile_to_program(ast)
216
0
            .map(|tokens| tokens.to_string())
217
0
            .with_context(|| "Failed to transpile to Rust")
218
    }
219
0
}
220
221
/// Write output to file or stdout (complexity: 5)
222
0
fn write_output(rust_code: &str, output: Option<&Path>, verbose: bool) -> Result<()> {
223
0
    if let Some(output_path) = output {
224
0
        fs::write(output_path, rust_code)
225
0
            .with_context(|| format!("Failed to write output file: {}", output_path.display()))?;
226
0
        if verbose {
227
0
            eprintln!("Output written to: {}", output_path.display());
228
0
        }
229
0
    } else {
230
0
        print!("{rust_code}");
231
0
    }
232
0
    Ok(())
233
0
}
234
235
/// Handle run command - compile and execute a Ruchy file
236
0
pub fn handle_run_command(file: &Path, verbose: bool) -> Result<()> {
237
0
    log_run_start(file, verbose);
238
    
239
    // Parse and transpile
240
0
    let source = fs::read_to_string(file)
241
0
        .with_context(|| format!("Failed to read file: {}", file.display()))?;
242
0
    let ast = parse_source(&source)?;
243
0
    let rust_code = transpile_for_execution(&ast, file)?;
244
    
245
    // Compile and execute
246
0
    let (temp_source, binary_path) = prepare_compilation(&rust_code, verbose)?;
247
0
    compile_rust_code(temp_source.path(), &binary_path)?;
248
0
    execute_binary(&binary_path)?;
249
    
250
0
    Ok(())
251
0
}
252
253
/// Log run command start (complexity: 2)
254
0
fn log_run_start(file: &Path, verbose: bool) {
255
0
    if verbose {
256
0
        eprintln!("Running file: {}", file.display());
257
0
    }
258
0
}
259
260
/// Transpile AST for execution with context (complexity: 3)
261
0
fn transpile_for_execution(ast: &Expr, file: &Path) -> Result<String> {
262
0
    let transpiler = Transpiler::new();
263
0
    transpiler.transpile_to_program_with_context(ast, Some(file))
264
0
        .map(|tokens| tokens.to_string())
265
0
        .with_context(|| "Failed to transpile to Rust")
266
0
}
267
268
/// Prepare compilation artifacts (complexity: 4)
269
0
fn prepare_compilation(rust_code: &str, verbose: bool) -> Result<(tempfile::NamedTempFile, PathBuf)> {
270
0
    let temp_source = tempfile::NamedTempFile::new()
271
0
        .with_context(|| "Failed to create temporary file")?;
272
0
    fs::write(temp_source.path(), rust_code)
273
0
        .with_context(|| "Failed to write temporary file")?;
274
    
275
0
    if verbose {
276
0
        eprintln!("Temporary Rust file: {}", temp_source.path().display());
277
0
        eprintln!("Compiling and running...");
278
0
    }
279
    
280
    // Create unique binary path using process ID for temporary compilation output
281
0
    let binary_path = std::env::temp_dir().join(format!("ruchy_temp_bin_{}", std::process::id()));
282
    
283
0
    Ok((temp_source, binary_path))
284
0
}
285
286
/// Compile Rust code using rustc (complexity: 5)
287
0
fn compile_rust_code(source_path: &Path, binary_path: &Path) -> Result<()> {
288
0
    let output = std::process::Command::new("rustc")
289
0
        .arg("--edition=2021")
290
0
        .arg("--crate-name=ruchy_temp")
291
0
        .arg("-o")
292
0
        .arg(binary_path)
293
0
        .arg(source_path)
294
0
        .output()
295
0
        .with_context(|| "Failed to run rustc")?;
296
    
297
0
    if !output.status.success() {
298
0
        eprintln!("Compilation failed:");
299
0
        eprintln!("{}", String::from_utf8_lossy(&output.stderr));
300
0
        std::process::exit(1);
301
0
    }
302
    
303
0
    Ok(())
304
0
}
305
306
/// Execute compiled binary and handle output (complexity: 5)
307
0
fn execute_binary(binary_path: &Path) -> Result<()> {
308
0
    let run_output = std::process::Command::new(binary_path)
309
0
        .output()
310
0
        .with_context(|| "Failed to run compiled binary")?;
311
    
312
0
    print!("{}", String::from_utf8_lossy(&run_output.stdout));
313
0
    if !run_output.stderr.is_empty() {
314
0
        eprint!("{}", String::from_utf8_lossy(&run_output.stderr));
315
0
    }
316
    
317
0
    if !run_output.status.success() {
318
0
        std::process::exit(run_output.status.code().unwrap_or(1));
319
0
    }
320
    
321
0
    Ok(())
322
0
}
323
324
/// Handle interactive theorem prover (RUCHY-0820) - delegated to refactored module
325
0
pub fn handle_prove_command(
326
0
    file: Option<&std::path::Path>,
327
0
    backend: &str,
328
0
    ml_suggestions: bool,
329
0
    timeout: u64,
330
0
    script: Option<&std::path::Path>,
331
0
    export: Option<&std::path::Path>,
332
0
    check: bool,
333
0
    counterexample: bool,
334
0
    verbose: bool,
335
0
    format: &str,
336
0
) -> anyhow::Result<()> {
337
    // Delegate to refactored module with ≤10 complexity
338
0
    handlers_modules::prove::handle_prove_command(
339
0
        file,
340
0
        backend,
341
0
        ml_suggestions,
342
0
        timeout,
343
0
        script,
344
0
        export,
345
0
        check,
346
0
        counterexample,
347
0
        verbose,
348
0
        format,
349
    )
350
0
}
351
352
/// Print prover help - moved to separate function for clarity
353
0
fn print_prover_help() {
354
0
    println!("\nInteractive Prover Commands:");
355
0
    println!("  help          - Show this help message");
356
0
    println!("  quit/exit     - Exit the prover");
357
0
    println!("  goals         - Show current proof goals");
358
0
    println!("  tactics       - List available tactics");
359
0
    println!("  goal <stmt>   - Add a new proof goal");
360
0
    println!("  apply <tactic> - Apply a tactic to current goal");
361
0
    println!("\nTactics:");
362
0
    println!("  intro         - Introduce hypothesis from implication");
363
0
    println!("  split         - Split conjunction into subgoals");
364
0
    println!("  induction     - Proof by induction");
365
0
    println!("  contradiction - Proof by contradiction");
366
0
    println!("  reflexivity   - Prove equality by reflexivity");
367
0
    println!("  simplify      - Simplify expression");
368
0
    println!("  assumption    - Prove using an assumption");
369
0
    println!("\nExamples:");
370
0
    println!("  goal x > 0 -> x + 1 > 1");
371
0
    println!("  apply intro");
372
0
    println!("  apply simplify\n");
373
0
}
374
375
/// Handle REPL command - start the interactive Read-Eval-Print Loop
376
/// 
377
/// # Examples
378
/// ```
379
/// // This function is typically called when no command or when repl command is specified
380
/// // handle_repl_command();
381
/// ```
382
/// 
383
/// # Errors
384
/// Returns error if REPL fails to initialize or run
385
0
pub fn handle_repl_command(record_file: Option<PathBuf>) -> Result<()> {
386
    use colored::Colorize;
387
    
388
0
    let version_msg = format!("Welcome to Ruchy REPL v{}", env!("CARGO_PKG_VERSION"));
389
0
    println!("{}", version_msg.bright_cyan().bold());
390
0
    println!(
391
0
        "Type {} for commands, {} to exit\n",
392
0
        ":help".green(),
393
0
        ":quit".yellow()
394
    );
395
396
0
    let mut repl = Repl::new()?;
397
    
398
0
    if let Some(record_path) = record_file {
399
0
        repl.run_with_recording(&record_path)
400
    } else {
401
0
        repl.run()
402
    }
403
0
}
404
405
/// Handle compile command - compile Ruchy file to native binary
406
/// 
407
/// # Arguments
408
/// * `file` - Path to the Ruchy file to compile
409
/// * `output` - Output binary path
410
/// * `opt_level` - Optimization level (0, 1, 2, 3, s, z)
411
/// * `strip` - Strip debug symbols
412
/// * `static_link` - Use static linking
413
/// * `target` - Target triple for cross-compilation
414
/// 
415
/// # Examples
416
/// ```
417
/// // This function is typically called by the CLI compile command
418
/// // handle_compile_command(&Path::new("app.ruchy"), PathBuf::from("app"), "2".to_string(), true, false, None);
419
/// ```
420
/// 
421
/// # Errors
422
/// Returns error if compilation fails or rustc is not available
423
0
pub fn handle_compile_command(
424
0
    file: &Path,
425
0
    output: PathBuf,
426
0
    opt_level: String,
427
0
    strip: bool,
428
0
    static_link: bool,
429
0
    target: Option<String>,
430
0
) -> Result<()> {
431
    use ruchy::backend::{CompileOptions, compile_to_binary as backend_compile};
432
    use colored::Colorize;
433
    use std::fs;
434
    
435
    // Check if rustc is available
436
0
    if let Err(e) = ruchy::backend::compiler::check_rustc_available() {
437
0
        eprintln!("{} {}", "Error:".bright_red(), e);
438
0
        eprintln!("Please install Rust toolchain from https://rustup.rs/");
439
0
        std::process::exit(1);
440
0
    }
441
    
442
0
    println!("{} Compiling {}...", "→".bright_blue(), file.display());
443
    
444
0
    let options = CompileOptions {
445
0
        output,
446
0
        opt_level,
447
0
        strip,
448
0
        static_link,
449
0
        target,
450
0
        rustc_flags: Vec::new(),
451
0
    };
452
    
453
0
    match backend_compile(file, &options) {
454
0
        Ok(binary_path) => {
455
0
            println!(
456
0
                "{} Successfully compiled to: {}",
457
0
                "āœ“".bright_green(),
458
0
                binary_path.display()
459
            );
460
            
461
            // Make the binary executable on Unix
462
            #[cfg(unix)]
463
            {
464
                use std::os::unix::fs::PermissionsExt;
465
0
                let mut perms = fs::metadata(&binary_path)?.permissions();
466
0
                perms.set_mode(0o755);
467
0
                fs::set_permissions(&binary_path, perms)?;
468
            }
469
            
470
0
            println!(
471
0
                "{} Binary size: {} bytes",
472
0
                "ℹ".bright_blue(),
473
0
                fs::metadata(&binary_path)?.len()
474
            );
475
        }
476
0
        Err(e) => {
477
0
            eprintln!("{} Compilation failed: {}", "āœ—".bright_red(), e);
478
0
            std::process::exit(1);
479
        }
480
    }
481
    
482
0
    Ok(())
483
0
}
484
485
/// Handle check command - check syntax of a Ruchy file
486
/// 
487
/// # Arguments
488
/// * `file` - Path to the Ruchy file to check
489
/// * `watch` - Enable file watching mode
490
/// 
491
/// # Examples
492
/// ```
493
/// // This function is typically called by the CLI check command
494
/// // handle_check_command(&Path::new("script.ruchy"), false);
495
/// ```
496
/// 
497
/// # Errors
498
/// Returns error if file cannot be read or has syntax errors
499
0
pub fn handle_check_command(file: &Path, watch: bool) -> Result<()> {
500
0
    if watch {
501
0
        handle_watch_and_check(file)
502
    } else {
503
0
        handle_check_syntax(file)
504
    }
505
0
}
506
507
/// Check syntax of a single file
508
0
fn handle_check_syntax(file: &Path) -> Result<()> {
509
    use colored::Colorize;
510
    
511
0
    let source = fs::read_to_string(file)?;
512
0
    let mut parser = RuchyParser::new(&source);
513
0
    match parser.parse() {
514
        Ok(_) => {
515
0
            println!("{}", "āœ“ Syntax is valid".green());
516
0
            Ok(())
517
        }
518
0
        Err(e) => {
519
0
            eprintln!("{}", format!("āœ— Syntax error: {e}").red());
520
0
            std::process::exit(1);
521
        }
522
    }
523
0
}
524
525
/// Watch a file and check syntax on changes
526
0
fn handle_watch_and_check(file: &Path) -> Result<()> {
527
    use colored::Colorize;
528
    use std::thread;
529
    use std::time::Duration;
530
    
531
0
    println!(
532
0
        "{} Watching {} for changes...",
533
0
        "šŸ‘".bright_cyan(),
534
0
        file.display()
535
    );
536
0
    println!("Press Ctrl+C to stop watching\n");
537
538
    // Initial check
539
0
    handle_check_syntax(file)?;
540
541
    // Simple file watching using polling
542
0
    let mut last_modified = fs::metadata(file)?.modified()?;
543
544
    loop {
545
0
        thread::sleep(Duration::from_millis(500));
546
547
0
        let Ok(metadata) = fs::metadata(file) else {
548
0
            continue; // File might be temporarily unavailable
549
        };
550
551
0
        let Ok(modified) = metadata.modified() else {
552
0
            continue;
553
        };
554
555
0
        if modified != last_modified {
556
0
            last_modified = modified;
557
0
            println!("\n{} File changed, checking...", "→".bright_cyan());
558
0
            let _ = handle_check_syntax(file); // Don't exit on error, keep watching
559
0
        }
560
    }
561
0
}
562
563
/// Handle test command - run tests with various options (delegated to refactored module)
564
/// 
565
/// # Arguments
566
/// * `path` - Optional path to test directory
567
/// * `watch` - Enable watch mode
568
/// * `verbose` - Enable verbose output
569
/// * `filter` - Optional test filter
570
/// * `coverage` - Enable coverage reporting
571
/// * `coverage_format` - Coverage report format
572
/// * `parallel` - Number of parallel test threads
573
/// * `threshold` - Coverage threshold
574
/// * `format` - Output format
575
/// 
576
/// # Examples
577
/// ```
578
/// // This function is typically called by the CLI test command
579
/// // handle_test_command(None, false, false, None, false, &"text".to_string(), 1, 80.0, &"text".to_string());
580
/// ```
581
/// 
582
/// # Errors
583
/// Returns error if tests fail to run or coverage threshold is not met
584
0
pub fn handle_test_command(
585
0
    path: Option<PathBuf>,
586
0
    watch: bool,
587
0
    verbose: bool,
588
0
    filter: Option<&str>,
589
0
    coverage: bool,
590
0
    coverage_format: &str,
591
0
    parallel: usize,
592
0
    threshold: f64,
593
0
    format: &str,
594
0
) -> Result<()> {
595
    // Delegate to refactored module with ≤10 complexity
596
0
    handlers_modules::test::handle_test_command(
597
0
        path,
598
0
        watch,
599
0
        verbose,
600
0
        filter,
601
0
        coverage,
602
0
        coverage_format,
603
0
        parallel,
604
0
        threshold,
605
0
        format,
606
    )
607
0
}
608
609
/// Handle the coverage command - generate coverage report for Ruchy code
610
///
611
/// # Arguments
612
/// * `path` - File or directory path to analyze
613
/// * `threshold` - Coverage threshold to check against  
614
/// * `format` - Output format (text, html, json)
615
/// * `verbose` - Enable verbose output
616
///
617
/// # Errors
618
/// Returns error if coverage analysis fails or threshold is not met
619
0
pub fn handle_coverage_command(
620
0
    path: &Path,
621
0
    threshold: f64,
622
0
    format: &str,
623
0
    verbose: bool,
624
0
) -> Result<()> {
625
    use ruchy::quality::ruchy_coverage::RuchyCoverageCollector;
626
    
627
0
    if verbose {
628
0
        println!("šŸ” Analyzing coverage for: {}", path.display());
629
0
        println!("šŸ“Š Threshold: {:.1}%", threshold);
630
0
        println!("šŸ“‹ Format: {}", format);
631
0
    }
632
633
    // Create coverage collector
634
0
    let mut collector = RuchyCoverageCollector::new();
635
    
636
    // Execute the file with coverage collection
637
0
    collector.execute_with_coverage(path)?;
638
    
639
    // Generate the coverage report based on format
640
0
    let report = match format {
641
0
        "html" => collector.generate_html_report(),
642
0
        "json" => collector.generate_json_report(),
643
0
        _ => collector.generate_text_report(), // Default to text
644
    };
645
0
    println!("{}", report);
646
    
647
    // Check threshold if specified
648
0
    if threshold > 0.0 {
649
0
        if collector.meets_threshold(threshold) {
650
0
            println!("\nāœ… Coverage meets threshold of {:.1}%", threshold);
651
0
        } else {
652
0
            eprintln!("\nāŒ Coverage below threshold of {:.1}%", threshold);
653
0
            std::process::exit(1);
654
        }
655
0
    }
656
    
657
0
    Ok(())
658
0
}
659
660
/// Watch and run tests on changes - delegated to refactored module
661
0
fn handle_watch_and_test(path: &Path, verbose: bool, filter: Option<&str>) -> Result<()> {
662
0
    handlers_modules::test::handle_test_command(
663
0
        Some(path.to_path_buf()),
664
        true, // watch mode
665
0
        verbose,
666
0
        filter,
667
        false, // coverage
668
0
        "text",
669
        1,
670
        0.0,
671
0
        "text",
672
    )
673
0
}
674
675
/// Run enhanced tests - delegated to refactored module
676
#[allow(clippy::unnecessary_wraps)]
677
0
fn handle_run_enhanced_tests(
678
0
    path: &Path,
679
0
    verbose: bool,
680
0
    filter: Option<&str>,
681
0
    coverage: bool,
682
0
    coverage_format: &str,
683
0
    parallel: usize,
684
0
    threshold: f64,
685
0
    format: &str,
686
0
) -> Result<()> {
687
0
    handlers_modules::test::handle_test_command(
688
0
        Some(path.to_path_buf()),
689
        false, // not watch mode
690
0
        verbose,
691
0
        filter,
692
0
        coverage,
693
0
        coverage_format,
694
0
        parallel,
695
0
        threshold,
696
0
        format,
697
    )
698
0
}
699
700
/// Run a single .ruchy test file - delegated to `test_helpers` module
701
0
fn run_ruchy_test_file(test_file: &Path, verbose: bool) -> Result<()> {
702
0
    handlers_modules::test_helpers::run_test_file(test_file, verbose)
703
0
}
704
705
/// Verify proofs extracted from AST - delegated to `prove_helpers` module
706
0
fn verify_proofs_from_ast(
707
0
    ast: &ruchy::frontend::ast::Expr, 
708
0
    file_path: &std::path::Path, 
709
0
    format: &str,
710
0
    counterexample: bool, 
711
0
    verbose: bool
712
0
) -> Result<()> {
713
0
    handlers_modules::prove_helpers::verify_proofs_from_ast(
714
0
        ast,
715
0
        file_path,
716
0
        format,
717
0
        counterexample,
718
0
        verbose,
719
    )
720
0
}
721
/// 
722
/// # Arguments
723
/// * `command` - The command to execute
724
/// 
725
/// # Examples
726
/// ```
727
/// // This function is typically called by the main dispatcher for complex commands
728
/// ```
729
/// 
730
/// # Errors
731
/// Returns error if command execution fails
732
#[allow(clippy::unnecessary_wraps)]
733
0
pub fn handle_complex_command(command: crate::Commands) -> Result<()> {
734
0
    match command {
735
        crate::Commands::Ast { 
736
0
            file, 
737
0
            json, 
738
0
            graph, 
739
0
            metrics, 
740
0
            symbols, 
741
0
            deps, 
742
0
            verbose, 
743
0
            output 
744
        } => {
745
0
            commands::handle_ast_command(
746
0
                &file,
747
0
                json,
748
0
                graph,
749
0
                metrics,
750
0
                symbols,
751
0
                deps,
752
0
                verbose,
753
0
                output.as_deref(),
754
            )
755
        }
756
        crate::Commands::Provability { 
757
0
            file, 
758
0
            verify, 
759
0
            contracts, 
760
0
            invariants, 
761
0
            termination, 
762
0
            bounds, 
763
0
            verbose, 
764
0
            output 
765
        } => {
766
0
            commands::handle_provability_command(
767
0
                &file,
768
0
                verify,
769
0
                contracts,
770
0
                invariants,
771
0
                termination,
772
0
                bounds,
773
0
                verbose,
774
0
                output.as_deref(),
775
            )
776
        }
777
        crate::Commands::Runtime { 
778
0
            file, 
779
0
            profile, 
780
0
            bigo, 
781
0
            bench, 
782
0
            compare, 
783
0
            memory, 
784
0
            verbose, 
785
0
            output 
786
        } => {
787
0
            commands::handle_runtime_command(
788
0
                &file,
789
0
                profile,
790
0
                bigo,
791
0
                bench,
792
0
                compare.as_deref(),
793
0
                memory,
794
0
                verbose,
795
0
                output.as_deref(),
796
            )
797
        }
798
        crate::Commands::Score {
799
0
            path,
800
0
            depth,
801
0
            fast,
802
0
            deep,
803
0
            watch,
804
0
            explain,
805
0
            baseline,
806
0
            min,
807
0
            config,
808
0
            format,
809
0
            verbose,
810
0
            output,
811
        } => {
812
0
            commands::handle_score_command(
813
0
                &path,
814
0
                &depth,
815
0
                fast,
816
0
                deep,
817
0
                watch,
818
0
                explain,
819
0
                baseline.as_deref(),
820
0
                min,
821
0
                config.as_deref(),
822
0
                &format,
823
0
                verbose,
824
0
                output.as_deref(),
825
            )
826
        }
827
        crate::Commands::QualityGate {
828
0
            path,
829
0
            config,
830
            depth: _,
831
0
            fail_fast,
832
0
            format,
833
0
            export,
834
            ci: _,
835
0
            verbose,
836
        } => {
837
            // Simplified quality gate handling
838
0
            commands::handle_quality_gate_command(
839
0
                &path,
840
0
                config.as_deref(),
841
0
                fail_fast,    // Use as strict
842
0
                !verbose,     // Use as quiet
843
0
                format == "json",
844
0
                verbose,
845
0
                None,         // No output field
846
0
                export.as_deref(),
847
            )
848
        }
849
        crate::Commands::Fmt {
850
0
            file,
851
0
            all,
852
0
            check,
853
0
            stdout,
854
0
            diff,
855
0
            config,
856
            line_width: _,
857
            indent: _,
858
            use_tabs: _,
859
        } => {
860
            // Simplified fmt handling
861
0
            commands::handle_fmt_command(
862
0
                &file,
863
0
                check,
864
0
                !check && !stdout,  // write if not check or stdout
865
0
                config.as_deref(),
866
0
                all,
867
0
                diff,
868
0
                stdout,
869
                false,  // verbose not available
870
            )
871
        }
872
        crate::Commands::Lint {
873
0
            file,
874
            all: _,
875
0
            fix,
876
0
            strict,
877
0
            verbose,
878
0
            format,
879
0
            rules,
880
            deny_warnings: _,
881
            max_complexity: _,
882
0
            config,
883
0
            init_config,
884
        } => {
885
0
            if init_config {
886
                // Create default lint config
887
0
                println!("Creating default lint configuration...");
888
0
                Ok(())
889
0
            } else if let Some(file_path) = file {
890
0
                commands::handle_lint_command(
891
0
                    &file_path,
892
0
                    fix,
893
0
                    strict,
894
0
                    rules.as_deref(),
895
0
                    format == "json",
896
0
                    verbose,
897
0
                    None,  // ignore not available
898
0
                    config.as_deref(),
899
                )
900
            } else {
901
0
                eprintln!("Error: Either provide a file or use --all flag");
902
0
                std::process::exit(1);
903
            }
904
        }
905
        crate::Commands::Prove {
906
0
            file,
907
0
            backend,
908
0
            ml_suggestions,
909
0
            timeout,
910
0
            script,
911
0
            export,
912
0
            check,
913
0
            counterexample,
914
0
            verbose,
915
0
            format,
916
        } => {
917
0
            handle_prove_command(
918
0
                file.as_deref(),
919
0
                &backend,
920
0
                ml_suggestions,
921
0
                timeout,
922
0
                script.as_deref(),
923
0
                export.as_deref(),
924
0
                check,
925
0
                counterexample,
926
0
                verbose,
927
0
                &format,
928
            )
929
        }
930
0
        crate::Commands::Coverage { path, threshold, format, verbose } => {
931
0
            handle_coverage_command(&path, threshold.unwrap_or(80.0), &format, verbose)
932
        }
933
0
        crate::Commands::ReplayToTests { input, output, property_tests, benchmarks, timeout } => {
934
0
            handle_replay_to_tests_command(&input, output.as_deref(), property_tests, benchmarks, timeout)
935
        }
936
        _ => {
937
            // Other commands not yet implemented
938
0
            eprintln!("Command not yet implemented");
939
0
            Ok(())
940
        }
941
    }
942
    
943
    /*
944
    // Original complex command handling - commented out until handlers implemented
945
    match command {
946
        crate::Commands::Ast { 
947
            file, 
948
            json, 
949
            graph, 
950
            metrics, 
951
            symbols, 
952
            deps, 
953
            verbose, 
954
            output 
955
        } => {
956
            // AST analysis implementation planned
957
            eprintln!("AST analysis not yet implemented");
958
            Ok(())
959
        }
960
        crate::Commands::Provability { 
961
            file, 
962
            verify, 
963
            contracts, 
964
            invariants, 
965
            termination, 
966
            bounds, 
967
            verbose, 
968
            output 
969
        } => {
970
            // Provability analysis implementation planned
971
            eprintln!("Provability analysis not yet implemented");
972
            Ok(())
973
        }
974
        crate::Commands::Runtime { 
975
            file, 
976
            profile, 
977
            bigo, 
978
            bench, 
979
            compare, 
980
            memory, 
981
            verbose, 
982
            output 
983
        } => {
984
            // Runtime analysis implementation planned
985
            eprintln!("Runtime analysis not yet implemented");
986
            Ok(())
987
        }
988
        crate::Commands::Score {
989
            path,
990
            depth,
991
            fast,
992
            deep,
993
            watch,
994
            explain,
995
            baseline,
996
            min,
997
            config,
998
            format,
999
            verbose,
1000
            output,
1001
        } => {
1002
            let baseline_str = baseline.as_deref();
1003
            let config_str = config.as_ref().and_then(|p| p.to_str());
1004
            let output_str = output.as_ref().and_then(|p| p.to_str());
1005
            // Quality score calculation implementation planned
1006
            eprintln!("Quality score calculation not yet implemented");
1007
            Ok(())
1008
        }
1009
        crate::Commands::QualityGate {
1010
            path,
1011
            config,
1012
            depth,
1013
            fail_fast,
1014
            format,
1015
            export,
1016
            ci,
1017
            verbose,
1018
        } => {
1019
            // Quality gates implementation planned
1020
            eprintln!("Quality gates enforcement not yet implemented");
1021
            Ok(())
1022
        }
1023
        crate::Commands::Fmt {
1024
            file: _,
1025
            all: _,
1026
            check: _,
1027
            stdout: _,
1028
            diff: _,
1029
            config: _,
1030
            line_width: _,
1031
            indent: _,
1032
            use_tabs: _,
1033
        } => {
1034
            // Code formatting implementation planned
1035
            eprintln!("Code formatting not yet implemented");
1036
            Ok(())
1037
        }
1038
        crate::Commands::Doc {
1039
            path,
1040
            output,
1041
            format,
1042
            private,
1043
            open,
1044
            all,
1045
            verbose,
1046
        } => {
1047
            // Documentation generation implementation planned
1048
            eprintln!("Documentation generation not yet implemented");
1049
            Ok(())
1050
        }
1051
        crate::Commands::Bench {
1052
            file,
1053
            iterations,
1054
            warmup,
1055
            format,
1056
            output,
1057
            verbose,
1058
        } => {
1059
            crate::benchmark_ruchy_code(
1060
                &file,
1061
                iterations,
1062
                warmup,
1063
                &format,
1064
                output.as_deref(),
1065
                verbose,
1066
            )
1067
        }
1068
        crate::Commands::Lint {
1069
            file,
1070
            all: _,
1071
            fix,
1072
            strict,
1073
            verbose,
1074
            format,
1075
            rules,
1076
            deny_warnings: _,
1077
            max_complexity: _,
1078
            config,
1079
            init_config,
1080
        } => {
1081
            if init_config {
1082
                crate::generate_default_lint_config()
1083
            } else {
1084
                // Load custom rules if config provided
1085
                let custom_rules = if let Some(config_path) = config {
1086
                    crate::load_custom_lint_rules(&config_path)?
1087
                } else {
1088
                    // Custom lint rules implementation planned
1089
                    Default::default()
1090
                };
1091
1092
                if all {
1093
                    crate::lint_ruchy_code(
1094
                        &PathBuf::from("."),
1095
                        all,
1096
                        fix,
1097
                        strict,
1098
                        verbose,
1099
                        &format,
1100
                        rules.as_deref(),
1101
                        deny_warnings,
1102
                        max_complexity,
1103
                        &custom_rules,
1104
                    )
1105
                } else if let Some(file) = file {
1106
                    crate::lint_ruchy_code(
1107
                        &file,
1108
                        false,
1109
                        fix,
1110
                        strict,
1111
                        verbose,
1112
                        &format,
1113
                        rules.as_deref(),
1114
                        deny_warnings,
1115
                        max_complexity,
1116
                        &custom_rules,
1117
                    )
1118
                } else {
1119
                    eprintln!("Error: Either provide a file or use --all flag");
1120
                    std::process::exit(1);
1121
                }
1122
            }
1123
        }
1124
        crate::Commands::Add {
1125
            package,
1126
            version,
1127
            dev,
1128
            registry,
1129
        } => {
1130
            crate::add_package(&package, version.as_deref(), dev, &registry)
1131
        }
1132
        crate::Commands::Publish {
1133
            registry,
1134
            version,
1135
            dry_run,
1136
            allow_dirty,
1137
        } => {
1138
            crate::publish_package(&registry, version.as_deref(), dry_run, allow_dirty)
1139
        }
1140
        crate::Commands::Mcp {
1141
            name,
1142
            streaming,
1143
            timeout,
1144
            min_score,
1145
            max_complexity,
1146
            verbose,
1147
            config,
1148
        } => {
1149
            let config_str = config.as_ref().and_then(|p| p.to_str());
1150
            crate::start_mcp_server(&name, streaming, timeout, min_score, max_complexity, verbose, config_str)
1151
        }
1152
        crate::Commands::Optimize {
1153
            file,
1154
            hardware,
1155
            depth,
1156
            cache,
1157
            branches,
1158
            vectorization,
1159
            abstractions,
1160
            benchmark,
1161
            format,
1162
            output,
1163
            verbose,
1164
            threshold,
1165
        } => {
1166
            crate::optimize_file(
1167
                &file,
1168
                &hardware,
1169
                &depth,
1170
                cache,
1171
                branches,
1172
                vectorization,
1173
                abstractions,
1174
                benchmark,
1175
                &format,
1176
                output.as_deref(),
1177
                verbose,
1178
                threshold,
1179
            )
1180
        }
1181
        crate::Commands::ActorObserve {
1182
            config,
1183
            refresh_interval,
1184
            max_traces,
1185
            max_actors,
1186
            enable_deadlock_detection,
1187
            deadlock_interval,
1188
            start_mode,
1189
            no_color,
1190
            format,
1191
            export,
1192
            duration,
1193
            verbose,
1194
            filter_actor,
1195
            filter_failed,
1196
            filter_slow,
1197
        } => {
1198
            crate::start_actor_observatory(
1199
                config.as_ref(),
1200
                refresh_interval,
1201
                max_traces,
1202
                max_actors,
1203
                enable_deadlock_detection,
1204
                deadlock_interval,
1205
                &start_mode,
1206
                !no_color,
1207
                &format,
1208
                export.as_ref(),
1209
                duration,
1210
                verbose,
1211
                filter_actor.as_ref(),
1212
                filter_failed,
1213
                filter_slow,
1214
            )
1215
        }
1216
        crate::Commands::DataflowDebug {
1217
            config,
1218
            max_rows,
1219
            auto_materialize,
1220
            enable_profiling,
1221
            timeout,
1222
            track_memory,
1223
            compute_diffs,
1224
            sample_rate,
1225
            refresh_interval,
1226
            no_color,
1227
            format,
1228
            export,
1229
            verbose,
1230
            breakpoint,
1231
            start_mode,
1232
        } => {
1233
            crate::start_dataflow_debugger(
1234
                config.as_ref(),
1235
                max_rows,
1236
                auto_materialize,
1237
                enable_profiling,
1238
                timeout,
1239
                track_memory,
1240
                compute_diffs,
1241
                sample_rate,
1242
                refresh_interval,
1243
                !no_color,
1244
                &format,
1245
                export.as_ref(),
1246
                verbose,
1247
                &breakpoint,
1248
                &start_mode,
1249
            )
1250
        }
1251
        crate::Commands::Wasm { 
1252
            file,
1253
            output,
1254
            target,
1255
            wit,
1256
            deploy,
1257
            deploy_target,
1258
            portability,
1259
            opt_level,
1260
            debug,
1261
            simd,
1262
            threads,
1263
            component_model,
1264
            name,
1265
            version,
1266
            verbose,
1267
        } => {
1268
            crate::handle_wasm_command(
1269
                &file,
1270
                output.as_deref(),
1271
                &target,
1272
                wit,
1273
                deploy,
1274
                deploy_target.as_deref(),
1275
                portability,
1276
                &opt_level,
1277
                debug,
1278
                simd,
1279
                threads,
1280
                component_model,
1281
                name.as_deref(),
1282
                &version,
1283
                verbose,
1284
            )
1285
        }
1286
        _ => {
1287
            // This should not be reached since handled commands are processed elsewhere
1288
            eprintln!("Error: Command not implemented in complex handler");
1289
            std::process::exit(1);
1290
        }
1291
    }
1292
    */
1293
0
}
1294
1295
/// Handle replay-to-tests command - convert .replay files to regression tests
1296
/// 
1297
/// # Arguments
1298
/// * `input` - Input replay file or directory containing .replay files
1299
/// * `output` - Optional output test file path
1300
/// * `property_tests` - Whether to include property tests
1301
/// * `benchmarks` - Whether to include benchmarks
1302
/// * `timeout` - Test timeout in milliseconds
1303
/// 
1304
/// # Examples
1305
/// ```
1306
/// // Convert single replay file
1307
/// handle_replay_to_tests_command(Path::new("demo.replay"), None, true, false, 5000);
1308
/// 
1309
/// // Convert directory of replay files
1310
/// handle_replay_to_tests_command(Path::new("demos/"), Some(Path::new("tests/replays.rs")), true, true, 10000);
1311
/// ```
1312
/// 
1313
/// # Errors
1314
/// Returns error if replay files can't be read or test files can't be written
1315
1316
/// Setup conversion configuration for replay-to-test conversion (complexity: 4)
1317
0
fn setup_conversion_config(property_tests: bool, benchmarks: bool, timeout: u64) -> ConversionConfig {
1318
0
    ConversionConfig {
1319
0
        test_module_prefix: "replay_generated".to_string(),
1320
0
        include_property_tests: property_tests,
1321
0
        include_benchmarks: benchmarks,
1322
0
        timeout_ms: timeout,
1323
0
    }
1324
0
}
1325
1326
/// Determine output path, using default if none provided (complexity: 3)
1327
0
fn determine_output_path(output: Option<&Path>) -> &Path {
1328
0
    let default_output = Path::new("tests/generated_from_replays.rs");
1329
0
    output.unwrap_or(default_output)
1330
0
}
1331
1332
/// Validate that file has .replay extension (complexity: 3)
1333
0
fn validate_replay_file(path: &Path) -> Result<()> {
1334
0
    if path.extension().and_then(|s| s.to_str()) == Some("replay") {
1335
0
        Ok(())
1336
    } else {
1337
0
        eprintln!("āŒ Input file must have .replay extension");
1338
0
        Err(anyhow::anyhow!("Invalid file extension"))
1339
    }
1340
0
}
1341
1342
/// Process a single .replay file (complexity: 8)
1343
0
fn process_single_file(
1344
0
    input: &Path, 
1345
0
    converter: &ruchy::runtime::replay_converter::ReplayConverter,
1346
0
    all_tests: &mut Vec<ruchy::runtime::replay_converter::GeneratedTest>,
1347
0
    processed_files: &mut usize
1348
0
) -> Result<()> {
1349
    
1350
    
1351
0
    validate_replay_file(input)?;
1352
    
1353
0
    println!("šŸ“„ Processing replay file: {}", input.display());
1354
    
1355
0
    match converter.convert_file(input) {
1356
0
        Ok(tests) => {
1357
0
            println!("  āœ… Generated {} tests", tests.len());
1358
0
            all_tests.extend(tests);
1359
0
            *processed_files += 1;
1360
0
            Ok(())
1361
        }
1362
0
        Err(e) => {
1363
0
            eprintln!("  āŒ Failed to process {}: {}", input.display(), e);
1364
0
            Err(e)
1365
        }
1366
    }
1367
0
}
1368
1369
/// Process directory containing .replay files (complexity: 10)
1370
0
fn process_directory(
1371
0
    input: &Path,
1372
0
    converter: &ruchy::runtime::replay_converter::ReplayConverter, 
1373
0
    all_tests: &mut Vec<ruchy::runtime::replay_converter::GeneratedTest>,
1374
0
    processed_files: &mut usize
1375
0
) -> Result<()> {
1376
    
1377
    use std::fs;
1378
    
1379
0
    println!("šŸ“ Processing replay directory: {}", input.display());
1380
    
1381
    // Find all .replay files in directory
1382
0
    let replay_files: Vec<_> = fs::read_dir(input)?
1383
0
        .filter_map(|entry| {
1384
0
            let entry = entry.ok()?;
1385
0
            let path = entry.path();
1386
0
            if path.is_file() && path.extension()? == "replay" {
1387
0
                Some(path)
1388
            } else {
1389
0
                None
1390
            }
1391
0
        })
1392
0
        .collect();
1393
    
1394
0
    if replay_files.is_empty() {
1395
0
        println!("āš ļø  No .replay files found in directory");
1396
0
        return Ok(());
1397
0
    }
1398
    
1399
0
    println!("šŸ” Found {} replay files", replay_files.len());
1400
    
1401
0
    for replay_file in replay_files {
1402
0
        println!("šŸ“„ Processing: {}", replay_file.display());
1403
        
1404
0
        match converter.convert_file(&replay_file) {
1405
0
            Ok(tests) => {
1406
0
                println!("  āœ… Generated {} tests", tests.len());
1407
0
                all_tests.extend(tests);
1408
0
                *processed_files += 1;
1409
0
            }
1410
0
            Err(e) => {
1411
0
                eprintln!("  āš ļø  Failed to process {}: {}", replay_file.display(), e);
1412
0
                // Continue with other files instead of failing completely
1413
0
            }
1414
        }
1415
    }
1416
    
1417
0
    Ok(())
1418
0
}
1419
1420
/// Write test output to file, creating directories if needed (complexity: 4)
1421
0
fn write_test_output(
1422
0
    converter: &ruchy::runtime::replay_converter::ReplayConverter,
1423
0
    all_tests: &[ruchy::runtime::replay_converter::GeneratedTest],
1424
0
    output_path: &Path
1425
0
) -> Result<()> {
1426
    use std::fs;
1427
    use anyhow::Context;
1428
    
1429
    // Create output directory if needed
1430
0
    if let Some(parent) = output_path.parent() {
1431
0
        fs::create_dir_all(parent)?;
1432
0
    }
1433
    
1434
0
    println!("šŸ“ Writing tests to: {}", output_path.display());
1435
    
1436
0
    converter.write_tests(all_tests, output_path)
1437
0
        .context("Failed to write test file")?;
1438
    
1439
0
    Ok(())
1440
0
}
1441
1442
/// Generate comprehensive summary report of conversion results (complexity: 8)
1443
0
fn generate_summary_report(
1444
0
    all_tests: &[ruchy::runtime::replay_converter::GeneratedTest],
1445
0
    processed_files: usize
1446
0
) {
1447
    use colored::Colorize;
1448
    use std::collections::{HashMap, HashSet};
1449
    
1450
0
    println!("\n{}", "šŸŽ‰ Conversion Summary".bright_green().bold());
1451
0
    println!("=====================================");
1452
0
    println!("šŸ“Š Files processed: {}", processed_files);
1453
0
    println!("āœ… Tests generated: {}", all_tests.len());
1454
    
1455
    // Breakdown by test category
1456
0
    let mut category_counts = HashMap::new();
1457
0
    let mut coverage_areas = HashSet::new();
1458
    
1459
0
    for test in all_tests {
1460
0
        *category_counts.entry(&test.category).or_insert(0) += 1;
1461
0
        coverage_areas.extend(test.coverage_areas.iter().cloned());
1462
0
    }
1463
    
1464
0
    println!("\nšŸ“‹ Test Breakdown:");
1465
0
    for (category, count) in category_counts {
1466
0
        println!("   {:?}: {}", category, count);
1467
0
    }
1468
    
1469
0
    println!("\nšŸŽÆ Coverage Areas: {} unique areas", coverage_areas.len());
1470
0
    if !coverage_areas.is_empty() {
1471
0
        let mut areas: Vec<_> = coverage_areas.into_iter().collect();
1472
0
        areas.sort();
1473
0
        for area in areas.iter().take(10) {  // Show first 10
1474
0
            println!("   • {}", area);
1475
0
        }
1476
0
        if areas.len() > 10 {
1477
0
            println!("   ... and {} more", areas.len() - 10);
1478
0
        }
1479
0
    }
1480
    
1481
0
    println!("\nšŸ’” Next Steps:");
1482
0
    println!("   1. Run tests: cargo test");
1483
0
    println!("   2. Measure coverage: cargo test -- --test-threads=1");
1484
0
    println!("   3. Validate replay determinism");
1485
    
1486
0
    println!("\nšŸš€ {}", "Replay-to-test conversion complete!".bright_green());
1487
0
}
1488
1489
/// Process input path (file or directory) with replay files (complexity: 5)
1490
0
fn process_input_path(
1491
0
    input: &Path,
1492
0
    converter: &ruchy::runtime::replay_converter::ReplayConverter,
1493
0
    all_tests: &mut Vec<ruchy::runtime::replay_converter::GeneratedTest>,
1494
0
    processed_files: &mut usize
1495
0
) -> Result<()> {
1496
0
    if input.is_file() {
1497
0
        process_single_file(input, converter, all_tests, processed_files)
1498
0
    } else if input.is_dir() {
1499
0
        process_directory(input, converter, all_tests, processed_files)
1500
    } else {
1501
0
        eprintln!("āŒ Input path must be a file or directory");
1502
0
        Err(anyhow::anyhow!("Invalid input path"))
1503
    }
1504
0
}
1505
1506
/// Convert REPL replay files to regression tests (complexity: 7)
1507
0
pub fn handle_replay_to_tests_command(
1508
0
    input: &Path,
1509
0
    output: Option<&Path>,
1510
0
    property_tests: bool,
1511
0
    benchmarks: bool,
1512
0
    timeout: u64,
1513
0
) -> Result<()> {
1514
    use colored::Colorize;
1515
    use ruchy::runtime::replay_converter::ReplayConverter;
1516
    
1517
0
    println!("{}", "šŸ”„ Converting REPL replay files to regression tests".bright_cyan().bold());
1518
0
    println!("Input: {}", input.display());
1519
    
1520
0
    let config = setup_conversion_config(property_tests, benchmarks, timeout);
1521
0
    let converter = ReplayConverter::with_config(config);
1522
0
    let mut all_tests = Vec::new();
1523
0
    let mut processed_files = 0;
1524
    
1525
0
    let output_path = determine_output_path(output);
1526
    
1527
0
    process_input_path(input, &converter, &mut all_tests, &mut processed_files)?;
1528
    
1529
0
    if all_tests.is_empty() {
1530
0
        println!("āš ļø  No tests generated");
1531
0
        return Ok(());
1532
0
    }
1533
    
1534
0
    write_test_output(&converter, &all_tests, output_path)?;
1535
0
    generate_summary_report(&all_tests, processed_files);
1536
    
1537
0
    Ok(())
1538
0
}