use shlesha::{
modules::profiler::{OptimizationGenerator, ProfilerConfig},
Shlesha,
};
use std::fs;
use std::path::PathBuf;
use std::time::Instant;
#[derive(Debug)]
enum Command {
Profile {
input_file: String,
from_script: String,
to_script: String,
output_dir: Option<String>,
},
GenerateOptimizations {
profile_dir: String,
output_dir: String,
},
Benchmark {
optimization_file: String,
test_file: String,
},
Stats {
profile_dir: String,
},
Help,
}
fn main() {
let args: Vec<String> = std::env::args().collect();
if args.len() < 2 {
print_help();
return;
}
let command = match parse_args(&args) {
Ok(cmd) => cmd,
Err(e) => {
eprintln!("Error: {}", e);
print_help();
return;
}
};
match execute_command(command) {
Ok(_) => {}
Err(e) => {
eprintln!("Error: {}", e);
std::process::exit(1);
}
}
}
fn parse_args(args: &[String]) -> Result<Command, String> {
match args[1].as_str() {
"profile" => {
if args.len() < 5 {
return Err(
"Profile command requires: input_file from_script to_script [output_dir]"
.to_string(),
);
}
Ok(Command::Profile {
input_file: args[2].clone(),
from_script: args[3].clone(),
to_script: args[4].clone(),
output_dir: args.get(5).cloned(),
})
}
"generate" => {
if args.len() < 4 {
return Err("Generate command requires: profile_dir output_dir".to_string());
}
Ok(Command::GenerateOptimizations {
profile_dir: args[2].clone(),
output_dir: args[3].clone(),
})
}
"benchmark" => {
if args.len() < 4 {
return Err("Benchmark command requires: optimization_file test_file".to_string());
}
Ok(Command::Benchmark {
optimization_file: args[2].clone(),
test_file: args[3].clone(),
})
}
"stats" => {
if args.len() < 3 {
return Err("Stats command requires: profile_dir".to_string());
}
Ok(Command::Stats {
profile_dir: args[2].clone(),
})
}
"help" | "--help" | "-h" => Ok(Command::Help),
_ => Err(format!("Unknown command: {}", args[1])),
}
}
fn execute_command(command: Command) -> Result<(), Box<dyn std::error::Error>> {
match command {
Command::Profile {
input_file,
from_script,
to_script,
output_dir,
} => {
profile_file(input_file, from_script, to_script, output_dir)?;
}
Command::GenerateOptimizations {
profile_dir,
output_dir,
} => {
generate_optimizations(profile_dir, output_dir)?;
}
Command::Benchmark {
optimization_file,
test_file,
} => {
benchmark_optimization(optimization_file, test_file)?;
}
Command::Stats { profile_dir } => {
show_stats(profile_dir)?;
}
Command::Help => {
print_help();
}
}
Ok(())
}
fn profile_file(
input_file: String,
from_script: String,
to_script: String,
output_dir: Option<String>,
) -> Result<(), Box<dyn std::error::Error>> {
println!("Profiling file: {}", input_file);
println!("Conversion: {} -> {}", from_script, to_script);
let text = fs::read_to_string(&input_file)?;
let mut config = ProfilerConfig::default();
if let Some(dir) = output_dir {
config.profile_dir = PathBuf::from(dir);
}
let mut transliterator = Shlesha::new();
transliterator.enable_profiling_with_config(config);
let start = Instant::now();
let result = transliterator.transliterate(&text, &from_script, &to_script)?;
let processing_time = start.elapsed();
println!("Processing completed in: {:?}", processing_time);
println!("Input length: {} characters", text.len());
println!("Output length: {} characters", result.len());
transliterator.save_profiles();
if let Some(stats) = transliterator.get_profile_stats() {
for ((from, to), profile_stats) in stats {
println!("\nProfile for {} -> {}:", from, to);
println!(
" Total sequences profiled: {}",
profile_stats.total_sequences_profiled
);
println!(" Unique sequences: {}", profile_stats.unique_sequences);
println!(" Top 10 sequences:");
for (i, (seq, count)) in profile_stats.top_sequences.iter().enumerate() {
if i >= 10 {
break;
}
println!(" {}: {} ({}x)", i + 1, seq, count);
}
}
}
println!("\nProfileing completed. Profile data saved to profiles directory.");
Ok(())
}
fn generate_optimizations(
profile_dir: String,
output_dir: String,
) -> Result<(), Box<dyn std::error::Error>> {
println!("Generating optimizations from profiles in: {}", profile_dir);
let mut config = ProfilerConfig::default();
config.profile_dir = PathBuf::from(&profile_dir);
config.optimization_dir = PathBuf::from(&output_dir);
let mut transliterator = Shlesha::new();
transliterator.enable_profiling_with_config(config);
let optimizations = transliterator.generate_optimizations();
if optimizations.is_empty() {
println!(
"No optimizations generated. Check that profile directory contains valid profiles."
);
return Ok(());
}
let _generator = OptimizationGenerator::new();
let mut generated_count = 0;
for optimization in &optimizations {
println!(
"Generated optimization for {} -> {}",
optimization.from_script, optimization.to_script
);
println!(" Sequences: {}", optimization.sequence_mappings.len());
println!(" Words: {}", optimization.word_mappings.len());
println!(" Total entries: {}", optimization.metadata.sequence_count);
let filename = format!(
"{}_{}_opt.json",
optimization.from_script, optimization.to_script
);
let output_path = PathBuf::from(&output_dir).join(filename);
let json = serde_json::to_string_pretty(optimization)?;
fs::write(&output_path, json)?;
generated_count += 1;
}
println!(
"\nGenerated {} optimization files in: {}",
generated_count, output_dir
);
Ok(())
}
fn benchmark_optimization(
optimization_file: String,
test_file: String,
) -> Result<(), Box<dyn std::error::Error>> {
println!("Benchmarking optimization: {}", optimization_file);
println!("Test file: {}", test_file);
let opt_content = fs::read_to_string(&optimization_file)?;
let optimization: shlesha::modules::profiler::OptimizedLookupTable =
serde_json::from_str(&opt_content)?;
let test_text = fs::read_to_string(&test_file)?;
let generator = OptimizationGenerator::new();
let benchmark = generator.benchmark_optimization(&optimization, &test_text);
println!("\nBenchmark Results:");
println!(" Baseline time: {} ns", benchmark.baseline_time_ns);
println!(" Optimized time: {} ns", benchmark.optimized_time_ns);
println!(" Speedup factor: {:.2}x", benchmark.speedup_factor);
println!(
" Cache hits: {}/{} sequences",
benchmark.cache_hits, benchmark.total_sequences
);
println!(
" Cache hit rate: {:.1}%",
(benchmark.cache_hits as f64 / benchmark.total_sequences as f64) * 100.0
);
if benchmark.speedup_factor > 1.0 {
println!(
" ✓ Optimization provides {:.1}% speedup",
(benchmark.speedup_factor - 1.0) * 100.0
);
} else if benchmark.speedup_factor < 1.0 {
println!(
" âš Optimization is {:.1}% slower",
(1.0 - benchmark.speedup_factor) * 100.0
);
} else {
println!(" → No significant performance difference");
}
Ok(())
}
fn show_stats(profile_dir: String) -> Result<(), Box<dyn std::error::Error>> {
println!("Profile statistics for: {}", profile_dir);
let profile_path = PathBuf::from(&profile_dir);
if !profile_path.exists() {
println!("Profile directory does not exist: {}", profile_dir);
return Ok(());
}
let mut total_profiles = 0;
let mut total_sequences = 0;
let mut total_conversions = 0;
for entry in fs::read_dir(&profile_path)? {
let entry = entry?;
let path = entry.path();
if path.extension().and_then(|s| s.to_str()) == Some("json") {
if let Ok(content) = fs::read_to_string(&path) {
if let Ok(profile) =
serde_json::from_str::<shlesha::modules::profiler::ConversionProfile>(&content)
{
total_profiles += 1;
total_sequences += profile.sequences.len();
total_conversions += profile.total_conversions;
println!(
"\nProfile: {} -> {}",
profile.from_script, profile.to_script
);
println!(" Conversions: {}", profile.total_conversions);
println!(" Unique sequences: {}", profile.sequences.len());
println!(" Created: {:?}", profile.created_at);
println!(" Updated: {:?}", profile.updated_at);
let mut sequences: Vec<_> = profile.sequences.iter().collect();
sequences.sort_by_key(|(_, stats)| std::cmp::Reverse(stats.count));
println!(" Top sequences:");
for (i, (seq, stats)) in sequences.iter().take(5).enumerate() {
println!(
" {}: '{}' ({}x, avg: {:.0}ns)",
i + 1,
seq,
stats.count,
stats.avg_processing_ns
);
}
}
}
}
}
println!("\n=== Summary ===");
println!("Total profiles: {}", total_profiles);
println!("Total unique sequences: {}", total_sequences);
println!("Total conversions recorded: {}", total_conversions);
if total_profiles > 0 {
println!(
"Average sequences per profile: {:.1}",
total_sequences as f64 / total_profiles as f64
);
println!(
"Average conversions per profile: {:.1}",
total_conversions as f64 / total_profiles as f64
);
}
Ok(())
}
fn print_help() {
println!("Shlesha Profile-Guided Optimization Tool");
println!();
println!("USAGE:");
println!(" profiler_cli <command> [options]");
println!();
println!("COMMANDS:");
println!(" profile <input_file> <from_script> <to_script> [output_dir]");
println!(" Profile a text file and collect usage statistics");
println!(" Example: profiler_cli profile bhagavad_gita.txt devanagari iast");
println!();
println!(" generate <profile_dir> <output_dir>");
println!(" Generate optimization tables from collected profiles");
println!(" Example: profiler_cli generate profiles optimizations");
println!();
println!(" benchmark <optimization_file> <test_file>");
println!(" Benchmark the effectiveness of an optimization");
println!(" Example: profiler_cli benchmark devanagari_iast_opt.json test.txt");
println!();
println!(" stats <profile_dir>");
println!(" Show statistics about collected profiles");
println!(" Example: profiler_cli stats profiles");
println!();
println!(" help");
println!(" Show this help message");
println!();
println!("SUPPORTED SCRIPTS:");
println!(" devanagari, iast, iso15919, itrans, slp1, harvard_kyoto, velthuis, wx");
println!(" bengali, telugu, tamil, kannada, malayalam, gujarati, gurmukhi, odia");
}