// CLI Tool Example - File processor with concurrency
use std::fs
use std::path::Path
use clap::Parser
use std::sync::mpsc
@command(
name: "wj-process",
about: "Process text files concurrently",
version: "1.0"
)
@derive(Clone)
struct Args {
@arg(help: "Input files to process")
files: Vec<string>,
@arg(short: 'o', long: "output", help: "Output directory")
output_dir: Option<string>,
@arg(short: 'w', long: "workers", default_value: "4", help: "Number of worker threads")
workers: int,
@arg(short: 'v', long: "verbose", help: "Verbose output")
verbose: bool,
@arg(long: "uppercase", help: "Convert to uppercase")
uppercase: bool,
@arg(long: "reverse", help: "Reverse lines")
reverse: bool,
}
struct ProcessResult {
input_file: string,
output_file: string,
lines_processed: int,
}
fn process_file(path: string, args: &Args) -> Result<ProcessResult, string> {
if args.verbose {
println("Processing: {}", path)
}
// Read the file
let content = fs.read_to_string(&path)?
let mut lines: Vec<string> = content.lines().map(|l| l).collect()
// Apply transformations
if args.uppercase {
lines = lines.iter().map(|l| l.to_uppercase()).collect()
}
if args.reverse {
lines.reverse()
}
let processed = lines.join("\n")
// Determine output path
let output_path = match &args.output_dir {
Some(dir) => {
let filename = Path::new(&path).file_name().unwrap()
format!("{}/{}", dir, filename.to_string_lossy())
}
None => {
format!("{}.processed", path)
}
}
// Write output
fs.write(&output_path, processed)?
Ok(ProcessResult {
input_file: path,
output_file: output_path,
lines_processed: lines.len() as int,
})
}
fn main() -> Result<(), string> {
let args = Args.parse()
// Create output directory if needed
match &args.output_dir {
Some(dir) => fs.create_dir_all(dir)?,
None => ()
}
if args.verbose {
println("Processing {} files with {} workers", args.files.len(), args.workers)
}
// Process files concurrently using channels
let (tx, rx) = mpsc::channel()
let file_count = args.files.len()
let files = args.files.clone()
// Spawn worker threads
for file in files {
let tx_clone = tx.clone()
let args_copy = args.clone()
thread {
let result = process_file(file, &args_copy)
tx_clone.send(result).unwrap()
}
}
// Drop the original sender so the channel closes when done
drop(tx)
// Collect results
let mut total_lines = 0
let mut success_count = 0
let mut error_count = 0
for result in rx {
match result {
Ok(res) => {
success_count += 1
total_lines += res.lines_processed
if args.verbose {
println("✓ {} -> {} ({} lines)",
res.input_file,
res.output_file,
res.lines_processed)
}
}
Err(e) => {
error_count += 1
eprintln("✗ Error: {}", e)
}
}
}
// Print summary
println("\nSummary:")
println(" Files processed: {}/{}", success_count, file_count)
println(" Total lines: {}", total_lines)
if error_count > 0 {
println(" Errors: {}", error_count)
}
Ok(())
}