#![warn(clippy::all)]
#![deny(unsafe_code)]
#[macro_use]
#[allow(unused_macros)]
mod generated_contracts;
mod oracle_integration;
mod repl;
use oracle_integration::{OracleOptions, OracleTranspileResult};
use anyhow::{Context, Result};
use clap::{Parser, Subcommand};
use std::fs;
use std::path::PathBuf;
#[derive(Parser, Debug)]
#[command(name = "decy")]
#[command(version = "0.2.0")]
#[command(about = "Transpile C code to safe Rust with minimal unsafe blocks", long_about = None)]
struct Cli {
#[command(subcommand)]
command: Option<Commands>,
}
#[derive(Subcommand, Debug)]
enum Commands {
Transpile {
#[arg(value_name = "FILE")]
input: PathBuf,
#[arg(short, long, value_name = "FILE")]
output: Option<PathBuf>,
#[arg(long)]
trace: bool,
#[arg(long)]
oracle: bool,
#[arg(long, default_value = "0.7")]
oracle_threshold: f32,
#[arg(long)]
auto_fix: bool,
#[arg(long)]
capture: bool,
#[arg(long, value_name = "FILE")]
import_patterns: Option<PathBuf>,
#[arg(long, value_name = "FORMAT")]
oracle_report: Option<String>,
#[arg(long)]
verify: bool,
},
TranspileProject {
#[arg(value_name = "DIR")]
input: PathBuf,
#[arg(short, long, value_name = "DIR")]
output: PathBuf,
#[arg(long)]
no_cache: bool,
#[arg(short, long, conflicts_with = "quiet")]
verbose: bool,
#[arg(short, long, conflicts_with = "verbose")]
quiet: bool,
#[arg(long)]
dry_run: bool,
#[arg(long)]
stats: bool,
#[arg(long)]
oracle: bool,
#[arg(long, default_value = "0.7")]
oracle_threshold: f32,
#[arg(long)]
auto_fix: bool,
#[arg(long)]
capture: bool,
#[arg(long, value_name = "FILE")]
import_patterns: Option<PathBuf>,
#[arg(long, value_name = "FORMAT")]
oracle_report: Option<String>,
},
CheckProject {
#[arg(value_name = "DIR")]
input: PathBuf,
},
CacheStats {
#[arg(value_name = "DIR")]
input: PathBuf,
},
Repl,
Audit {
#[arg(value_name = "FILE")]
input: PathBuf,
#[arg(short, long)]
verbose: bool,
},
DiffTest {
#[arg(value_name = "FILE")]
input: PathBuf,
#[arg(long, default_value = "5")]
timeout: u64,
},
Oracle {
#[command(subcommand)]
action: OracleAction,
},
}
#[derive(Subcommand, Debug)]
enum OracleAction {
Bootstrap {
#[arg(long)]
dry_run: bool,
},
Seed {
#[arg(long, value_name = "FILE")]
from: PathBuf,
},
Stats {
#[arg(long, default_value = "markdown")]
format: String,
},
Retire {
#[arg(long)]
dry_run: bool,
#[arg(long, value_name = "FILE")]
archive_path: Option<PathBuf>,
},
Validate {
#[arg(value_name = "DIR")]
corpus: PathBuf,
},
Export {
#[arg(value_name = "FILE")]
output: PathBuf,
#[arg(long, default_value = "jsonl")]
format: String,
#[arg(long)]
with_card: bool,
},
Train {
#[arg(long, value_name = "DIR")]
corpus: PathBuf,
#[arg(long, default_value = "P0")]
tier: String,
#[arg(long)]
dry_run: bool,
},
GenerateTraces {
#[arg(long, value_name = "DIR")]
corpus: PathBuf,
#[arg(long, value_name = "FILE")]
output: PathBuf,
#[arg(long, default_value = "P0")]
tier: String,
#[arg(long)]
dry_run: bool,
},
Query {
#[arg(long, value_name = "CODE")]
error: String,
#[arg(long, value_name = "CONTEXT")]
context: Option<String>,
#[arg(long, default_value = "text")]
format: String,
},
}
fn main() -> Result<()> {
let result = run();
if let Err(ref err) = result {
if let Some(diag_err) = err.downcast_ref::<decy_parser::DiagnosticError>() {
for diag in &diag_err.diagnostics {
eprintln!("{diag}");
}
std::process::exit(1);
}
}
result
}
fn run() -> Result<()> {
let cli = Cli::parse();
match cli.command {
Some(Commands::Transpile {
input,
output,
trace,
oracle,
oracle_threshold,
auto_fix,
capture,
import_patterns,
oracle_report,
verify,
}) => {
let oracle_opts = OracleOptions::new(oracle, Some(oracle_threshold), auto_fix)
.with_capture(capture)
.with_import(import_patterns)
.with_report_format(oracle_report);
transpile_file(input, output, &oracle_opts, trace, verify)?;
}
Some(Commands::TranspileProject {
input,
output,
no_cache,
verbose,
quiet,
dry_run,
stats,
oracle,
oracle_threshold,
auto_fix,
capture,
import_patterns,
oracle_report,
}) => {
let oracle_opts = OracleOptions::new(oracle, Some(oracle_threshold), auto_fix)
.with_capture(capture)
.with_import(import_patterns)
.with_report_format(oracle_report);
transpile_project(
input,
output,
!no_cache,
verbose,
quiet,
dry_run,
stats,
&oracle_opts,
)?;
}
Some(Commands::CheckProject { input }) => {
check_project(input)?;
}
Some(Commands::CacheStats { input }) => {
show_cache_stats(input)?;
}
Some(Commands::Repl) => {
repl::run()?;
}
Some(Commands::Audit { input, verbose }) => {
audit_file(input, verbose)?;
}
Some(Commands::DiffTest { input, timeout }) => {
diff_test_file(input, timeout)?;
}
Some(Commands::Oracle { action }) => {
handle_oracle_command(action)?;
}
None => {
println!("Decy: C-to-Rust Transpiler with EXTREME Quality Standards");
println!("Version 0.1.0");
println!();
println!("Use 'decy --help' for usage information");
println!("Use 'decy transpile <file>' to transpile C code to Rust");
println!("Use 'decy transpile-project <dir> -o <output>' for entire projects");
println!("Use 'decy check-project <dir>' to verify build order");
println!("Use 'decy cache-stats <dir>' to view cache statistics");
println!("Use 'decy repl' to start interactive mode");
println!("Use 'decy audit <file>' to audit unsafe code in Rust files");
println!("Use 'decy diff-test <file>' to compare C vs transpiled Rust behavior");
}
}
Ok(())
}
fn transpile_file(
input: PathBuf,
output: Option<PathBuf>,
oracle_opts: &OracleOptions,
trace_enabled: bool,
verify: bool,
) -> Result<()> {
let c_code = fs::read_to_string(&input).with_context(|| {
format!(
"Failed to read input file: {}\n\nTry: Check that the file exists and is readable\n or: Verify the file path is correct",
input.display()
)
})?;
let base_dir = input.parent();
let (rust_code, oracle_result) = if oracle_opts.should_use_oracle() {
let result =
oracle_integration::transpile_with_oracle(&c_code, oracle_opts).with_context(|| {
format!("Oracle-assisted transpilation failed for {}", input.display())
})?;
let code = result.rust_code.clone();
(code, Some(result))
} else if trace_enabled {
let (code, trace_collector) =
decy_core::transpile_with_trace(&c_code).with_context(|| {
format!(
"Failed to transpile {}\n\nTry: Check if the C code has syntax errors\n or: Preprocess the file first: gcc -E {} -o preprocessed.c",
input.display(),
input.display()
)
})?;
eprintln!("{}", trace_collector.to_json());
(code, None)
} else {
let code = decy_core::transpile_with_includes(&c_code, base_dir).with_context(|| {
format!(
"Failed to transpile {}\n\nTry: Check if the C code has syntax errors\n or: Preprocess the file first: gcc -E {} -o preprocessed.c",
input.display(),
input.display()
)
})?;
(code, None)
};
if verify {
let result =
decy_verify::verify_compilation(&rust_code).context("Failed to verify compilation")?;
if result.success {
eprintln!("Compilation verified: output passes rustc type-check");
} else {
eprintln!("Compilation verification FAILED:");
for err in &result.errors {
eprintln!(" {}", err.message);
}
anyhow::bail!("Generated Rust does not compile ({} errors)", result.errors.len());
}
}
let has_main = rust_code.contains("fn main(");
match output {
Some(output_path) => {
fs::write(&output_path, &rust_code).with_context(|| {
format!("Failed to write output file: {}", output_path.display())
})?;
eprintln!("✓ Transpiled {} → {}", input.display(), output_path.display());
if let Some(ref result) = oracle_result {
print_oracle_stats(result, oracle_opts);
}
if !has_main {
eprintln!();
eprintln!("ℹ Note: No main function found in source.");
eprintln!(" To compile the output as a library, use:");
eprintln!(" rustc --crate-type=lib {}", output_path.display());
}
}
None => {
print!("{}", rust_code);
if let Some(ref result) = oracle_result {
print_oracle_stats(result, oracle_opts);
}
if !has_main {
eprintln!();
eprintln!("ℹ Note: No main function found in source.");
eprintln!(" To compile the output as a library, use:");
eprintln!(" rustc --crate-type=lib <output_file>");
}
}
}
Ok(())
}
fn print_oracle_stats(result: &OracleTranspileResult, opts: &OracleOptions) {
if let Some(ref format) = opts.report_format {
print_oracle_report(result, format);
return;
}
eprintln!();
eprintln!("=== Oracle Statistics ===");
if result.patterns_imported > 0 {
eprintln!("Patterns imported: {}", result.patterns_imported);
}
eprintln!("Queries: {}", result.oracle_queries);
eprintln!("Fixes applied: {}", result.fixes_applied);
eprintln!("Retries: {}", result.retries_used);
if result.patterns_captured > 0 {
eprintln!("Patterns captured: {}", result.patterns_captured);
}
if result.compilation_success {
eprintln!("✓ Compilation: SUCCESS");
} else {
eprintln!("✗ Compilation: FAILED");
if !result.remaining_errors.is_empty() {
eprintln!("Remaining errors: {}", result.remaining_errors.len());
for err in &result.remaining_errors {
eprintln!(" - {}", err);
}
}
}
}
#[cfg_attr(not(feature = "oracle"), allow(unused_variables))]
fn print_oracle_report(result: &OracleTranspileResult, format: &str) {
#[cfg(feature = "oracle")]
{
use decy_oracle::{CIReport, CIThresholds, OracleMetrics};
let metrics = OracleMetrics {
queries: result.oracle_queries as u64,
hits: result.fixes_applied as u64, misses: (result.oracle_queries - result.fixes_applied) as u64,
fixes_applied: result.fixes_applied as u64,
fixes_verified: if result.compilation_success {
result.fixes_applied as u64
} else {
0
},
patterns_captured: result.patterns_captured as u64,
..Default::default()
};
let report = CIReport::from_metrics(metrics, CIThresholds::default());
match format.to_lowercase().as_str() {
"json" => println!("{}", report.to_json()),
"markdown" | "md" => println!("{}", report.to_markdown()),
"prometheus" | "prom" => {
let m = &report.metrics;
println!("{}", m.to_prometheus());
}
_ => {
eprintln!("Unknown report format: {}. Use: json, markdown, prometheus", format);
}
}
}
#[cfg(not(feature = "oracle"))]
{
eprintln!("Oracle report format '{}' requires --features oracle", format);
}
}
fn audit_file(input: PathBuf, verbose: bool) -> Result<()> {
let rust_code = fs::read_to_string(&input)
.with_context(|| format!("Failed to read input file: {}", input.display()))?;
let report = decy_verify::audit_rust_code(&rust_code)
.with_context(|| format!("Failed to audit {}", input.display()))?;
println!();
println!("Unsafe Code Audit Report");
println!("========================");
println!("File: {}", input.display());
println!("Total Lines: {}", report.total_lines);
println!("Unsafe Lines: {}", report.unsafe_lines);
println!(
"Unsafe Density: {:.2}% {}",
report.unsafe_density_percent,
if report.meets_density_target() { "✅ (Target: <5%)" } else { "❌ (Target: <5%)" }
);
println!();
if report.unsafe_blocks.is_empty() {
println!("✅ No unsafe blocks found - code is 100% safe!");
return Ok(());
}
println!("Unsafe Blocks Found: {}", report.unsafe_blocks.len());
println!("Average Confidence: {:.1}/100", report.average_confidence);
println!();
let high_conf = report.high_confidence_blocks();
if !high_conf.is_empty() {
println!("⚠️ {} blocks with HIGH confidence for elimination (≥70):", high_conf.len());
println!();
}
if verbose {
println!("Detailed Block Analysis:");
println!("------------------------");
for (idx, block) in report.unsafe_blocks.iter().enumerate() {
println!();
println!(
"{}. Line {} [Confidence: {}/100 - {}]",
idx + 1,
if block.line > 0 { block.line.to_string() } else { "N/A".to_string() },
block.confidence,
if block.confidence >= 70 {
"HIGH"
} else if block.confidence >= 40 {
"MEDIUM"
} else {
"LOW"
}
);
println!(" Pattern: {:?}", block.pattern);
println!(" Suggestion: {}", block.suggestion);
}
} else {
println!("Summary by Confidence:");
let high = report.unsafe_blocks.iter().filter(|b| b.confidence >= 70).count();
let medium =
report.unsafe_blocks.iter().filter(|b| b.confidence >= 40 && b.confidence < 70).count();
let low = report.unsafe_blocks.iter().filter(|b| b.confidence < 40).count();
println!(" HIGH (≥70): {} blocks - likely can be eliminated", high);
println!(" MEDIUM (40-69): {} blocks - review possible alternatives", medium);
println!(" LOW (<40): {} blocks - may require unsafe", low);
println!();
println!("Use --verbose flag to see detailed information for each block");
}
println!();
println!("---");
println!("Recommendation: Focus on eliminating HIGH confidence blocks first.");
println!();
Ok(())
}
fn diff_test_file(input: PathBuf, timeout_secs: u64) -> Result<()> {
use decy_verify::diff_test::{diff_test, DiffTestConfig};
let c_code = fs::read_to_string(&input).with_context(|| {
format!(
"Failed to read input file: {}\n\nTry: Check that the file exists and is readable",
input.display()
)
})?;
let base_dir = input.parent();
let rust_code = decy_core::transpile_with_includes(&c_code, base_dir).with_context(|| {
format!(
"Failed to transpile {}\n\nTry: Check if the C code has syntax errors",
input.display()
)
})?;
let config = DiffTestConfig { timeout_secs, ..Default::default() };
let result = diff_test(&c_code, &rust_code, &config)?;
if result.passed() {
println!("PASS: {} — C and Rust outputs are identical", input.display());
println!(
" stdout: {} bytes | exit code: {}",
result.c_output.stdout.len(),
result.c_output.exit_code
);
} else {
println!("FAIL: {} — behavioral divergence detected", input.display());
for divergence in &result.divergences {
println!(" {}", divergence);
}
anyhow::bail!("Differential test failed: {} divergence(s)", result.divergences.len());
}
Ok(())
}
#[allow(clippy::too_many_arguments)]
fn transpile_project(
input_dir: PathBuf,
output_dir: PathBuf,
use_cache: bool,
verbose: bool,
quiet: bool,
dry_run: bool,
stats: bool,
_oracle_opts: &OracleOptions,
) -> Result<()> {
use decy_core::{DependencyGraph, TranspilationCache};
use indicatif::{ProgressBar, ProgressStyle};
use std::time::Instant;
use walkdir::WalkDir;
if !input_dir.exists() {
anyhow::bail!(
"Input directory not found: {}\n\nTry: Check the path is correct\n or: Use 'decy check-project <dir>' to verify project structure",
input_dir.display()
);
}
if !dry_run {
fs::create_dir_all(&output_dir).with_context(|| {
format!("Failed to create output directory: {}", output_dir.display())
})?;
}
let c_files: Vec<PathBuf> = WalkDir::new(&input_dir)
.into_iter()
.filter_map(|e| e.ok())
.filter(|e| {
matches!(
e.path().extension().and_then(|s| s.to_str()),
Some("c") | Some("cu") | Some("cpp") | Some("cc") | Some("cxx")
)
})
.map(|e| e.path().to_path_buf())
.collect();
if c_files.is_empty() {
if !quiet {
println!("No C/C++/CUDA files found in {}", input_dir.display());
}
return Ok(());
}
if dry_run && !quiet {
println!("DRY RUN MODE - No files will be written");
println!();
}
if !quiet {
println!("Found {} C files to transpile", c_files.len());
println!();
}
let cache_dir = input_dir.join(".decy").join("cache");
if !dry_run {
fs::create_dir_all(&cache_dir)?;
}
let mut cache = if use_cache && !dry_run {
TranspilationCache::load(&cache_dir)?
} else {
TranspilationCache::new()
};
let mut dep_graph = DependencyGraph::new();
for file in &c_files {
dep_graph.add_file(file);
}
let build_order = dep_graph
.topological_sort()
.with_context(|| "Failed to compute build order (circular dependencies?)")?;
let pb = if quiet {
ProgressBar::hidden()
} else {
let pb = ProgressBar::new(c_files.len() as u64);
pb.set_style(
ProgressStyle::default_bar()
.template("[{elapsed_precise}] {bar:40.cyan/blue} {pos}/{len} {msg}")
.expect("valid progress template")
.progress_chars("=>-"),
);
pb
};
let start_time = Instant::now();
let mut transpiled_count = 0;
let mut cached_count = 0;
let mut total_lines = 0;
for file_path in build_order {
let relative_path = file_path.strip_prefix(&input_dir).unwrap_or(&file_path);
pb.set_message(format!("Transpiling {}", relative_path.display()));
if use_cache {
if let Some(_cached) = cache.get(&file_path) {
if verbose {
println!("✓ Cached: {}", relative_path.display());
}
pb.set_message(format!("✓ Cached {}", relative_path.display()));
cached_count += 1;
pb.inc(1);
continue;
}
}
let c_code = fs::read_to_string(&file_path)
.with_context(|| format!("Failed to read {}", file_path.display()))?;
if dry_run {
if !quiet {
println!("Would transpile: {}", relative_path.display());
}
pb.set_message(format!("Would transpile {}", relative_path.display()));
pb.inc(1);
continue;
}
let rust_code = decy_core::transpile(&c_code)
.with_context(|| format!("Failed to transpile {}", file_path.display()))?;
total_lines += rust_code.lines().count();
let output_path = output_dir.join(relative_path).with_extension("rs");
if let Some(parent) = output_path.parent() {
fs::create_dir_all(parent)?;
}
fs::write(&output_path, &rust_code)
.with_context(|| format!("Failed to write {}", output_path.display()))?;
if verbose {
println!("✓ Transpiled: {} → {}", relative_path.display(), output_path.display());
}
if use_cache {
let transpiled = decy_core::TranspiledFile {
source_path: file_path.clone(),
rust_code: rust_code.clone(),
dependencies: vec![], functions_exported: vec![], ffi_declarations: String::new(), };
cache.insert(&file_path, &transpiled);
}
transpiled_count += 1;
pb.inc(1);
}
pb.finish_with_message("Done");
if use_cache && !dry_run {
cache.save()?;
}
let elapsed = start_time.elapsed();
if !quiet {
println!();
if dry_run {
println!(
"✓ Dry run complete - {} files checked in {:.2}s",
c_files.len(),
elapsed.as_secs_f64()
);
} else {
println!("✓ Transpiled {} files in {:.2}s", transpiled_count, elapsed.as_secs_f64());
}
}
if (stats || verbose) && !quiet {
println!();
println!("=== Statistics ===");
println!("Files found: {}", c_files.len());
println!("Files transpiled: {}", transpiled_count);
if cached_count > 0 {
println!("Files cached: {}", cached_count);
}
if total_lines > 0 {
println!("Lines generated: {}", total_lines);
}
println!("Time elapsed: {:.2}s", elapsed.as_secs_f64());
if use_cache {
let cache_stats = cache.statistics();
println!();
println!("=== Cache Statistics ===");
println!("Cache hits: {}", cache_stats.hits);
println!("Cache misses: {}", cache_stats.misses);
if cache_stats.hits + cache_stats.misses > 0 {
let hit_rate = (cache_stats.hits as f64
/ (cache_stats.hits + cache_stats.misses) as f64)
* 100.0;
println!("Hit rate: {:.1}%", hit_rate);
let speedup = if cache_stats.misses > 0 {
(cache_stats.hits + cache_stats.misses) as f64 / cache_stats.misses as f64
} else {
1.0
};
println!("Estimated speedup: {:.1}x", speedup);
}
}
}
if !quiet && !dry_run {
println!();
println!("Output directory: {}", output_dir.display());
}
Ok(())
}
fn check_project(input_dir: PathBuf) -> Result<()> {
use decy_core::DependencyGraph;
use walkdir::WalkDir;
if !input_dir.exists() {
anyhow::bail!("Input directory not found: {}", input_dir.display());
}
println!("Checking project: {}", input_dir.display());
println!();
let c_files: Vec<PathBuf> = WalkDir::new(&input_dir)
.into_iter()
.filter_map(|e| e.ok())
.filter(|e| e.path().extension().and_then(|s| s.to_str()) == Some("c"))
.map(|e| e.path().to_path_buf())
.collect();
if c_files.is_empty() {
println!("No C files found.");
return Ok(());
}
println!("Found {} C files:", c_files.len());
for file in &c_files {
let relative = file.strip_prefix(&input_dir).unwrap_or(file);
println!(" - {}", relative.display());
}
println!();
let mut dep_graph = DependencyGraph::new();
for file in &c_files {
dep_graph.add_file(file);
}
match dep_graph.topological_sort() {
Ok(build_order) => {
println!("✓ No circular dependencies detected");
println!();
println!("Build order:");
for (idx, file) in build_order.iter().enumerate() {
let relative = file.strip_prefix(&input_dir).unwrap_or(file);
println!(" {}. {}", idx + 1, relative.display());
}
}
Err(e) => {
println!("❌ Circular dependencies detected:");
println!(" {}", e);
return Err(e);
}
}
println!();
println!("Project is ready for transpilation.");
Ok(())
}
fn show_cache_stats(input_dir: PathBuf) -> Result<()> {
use decy_core::TranspilationCache;
if !input_dir.exists() {
anyhow::bail!("Input directory not found: {}", input_dir.display());
}
let cache_dir = input_dir.join(".decy").join("cache");
if !cache_dir.exists() {
println!("No cache found at {}", input_dir.display());
println!();
println!("Run 'decy transpile-project' with caching enabled to create a cache.");
return Ok(());
}
let cache = TranspilationCache::load(&cache_dir)?;
println!("Cache Statistics");
println!("================");
println!("Location: {}", input_dir.join(".decy/cache").display());
println!();
let stats = cache.statistics();
let total = stats.hits + stats.misses;
println!("Total files cached: {}", stats.total_files);
println!("Total requests: {}", total);
println!("Cache hits: {}", stats.hits);
println!("Cache misses: {}", stats.misses);
if total > 0 {
let hit_rate = (stats.hits as f64 / total as f64) * 100.0;
println!("Hit rate: {:.1}%", hit_rate);
if hit_rate > 80.0 {
println!();
println!("✓ Excellent cache performance!");
} else if hit_rate > 50.0 {
println!();
println!("ℹ Good cache performance.");
} else if hit_rate > 0.0 {
println!();
println!("⚠ Low cache hit rate - files may be changing frequently.");
}
} else {
println!();
println!("No cache usage recorded yet.");
}
Ok(())
}
fn handle_oracle_command(action: OracleAction) -> Result<()> {
#[cfg(not(feature = "oracle"))]
{
let _ = action;
anyhow::bail!(
"Oracle commands require the 'oracle' feature.\n\nTry: cargo build -p decy --features oracle"
);
}
#[cfg(feature = "oracle")]
{
match action {
OracleAction::Bootstrap { dry_run } => handle_oracle_bootstrap(dry_run),
OracleAction::Seed { from } => handle_oracle_seed(from),
OracleAction::Stats { format } => handle_oracle_stats(format),
OracleAction::Retire { dry_run, archive_path } => {
handle_oracle_retire(dry_run, archive_path)
}
OracleAction::Validate { corpus } => handle_oracle_validate(corpus),
OracleAction::Export { output, format, with_card } => {
handle_oracle_export(output, format, with_card)
}
OracleAction::Train { corpus, tier, dry_run } => {
handle_oracle_train(corpus, tier, dry_run)
}
OracleAction::GenerateTraces { corpus, output, tier, dry_run } => {
handle_oracle_generate_traces(corpus, output, tier, dry_run)
}
OracleAction::Query { error, context, format } => {
handle_oracle_query(error, context, format)
}
}
}
}
#[cfg(feature = "oracle")]
fn handle_oracle_bootstrap(dry_run: bool) -> Result<()> {
use decy_oracle::bootstrap::{get_bootstrap_patterns, BootstrapStats};
use decy_oracle::{DecyOracle, OracleConfig};
println!("=== Oracle Bootstrap ===");
println!();
let stats = BootstrapStats::from_patterns();
println!("{}", stats.to_string_pretty());
if dry_run {
println!();
println!("DRY RUN MODE - Patterns shown but not saved");
println!();
println!("Available patterns:");
for p in get_bootstrap_patterns() {
println!(" [{}] {}: {}", p.error_code, p.decision, p.description);
}
return Ok(());
}
#[cfg(feature = "citl")]
{
let config = OracleConfig::default();
let mut oracle = DecyOracle::new(config)?;
let count = oracle.bootstrap()?;
oracle.save()?;
println!();
println!("✓ Bootstrapped oracle with {} patterns", count);
println!(
" Patterns saved to: {}",
OracleConfig::default().patterns_path.display()
);
}
#[cfg(not(feature = "citl"))]
{
println!();
println!("⚠ Pattern saving requires the 'citl' feature");
println!(" Bootstrap patterns shown above can be used manually");
}
Ok(())
}
#[cfg(feature = "oracle")]
fn handle_oracle_seed(from: PathBuf) -> Result<()> {
if !from.exists() {
anyhow::bail!(
"Pattern file not found: {}\n\nTry: Verify the path to the .apr file",
from.display()
);
}
#[cfg(feature = "citl")]
{
use decy_oracle::{DecyOracle, OracleConfig};
println!("Seeding oracle from: {}", from.display());
let config = OracleConfig::default();
let mut oracle = DecyOracle::new(config)?;
let (count, stats) = oracle.import_patterns_with_stats(
&from,
decy_oracle::SmartImportConfig::default(),
)?;
println!();
println!("=== Import Results ===");
println!("Patterns imported: {}", count);
println!("Total evaluated: {}", stats.total_evaluated);
println!("Acceptance rate: {:.1}%", stats.overall_acceptance_rate() * 100.0);
println!();
println!("Import statistics by strategy:");
for (strategy, count) in &stats.accepted_by_strategy {
let rejected =
stats.rejected_by_strategy.get(strategy).copied().unwrap_or(0);
let total = count + rejected;
println!(
" {:?}: {}/{} accepted ({:.1}%)",
strategy,
count,
total,
if total > 0 { (*count as f64 / total as f64) * 100.0 } else { 0.0 }
);
}
oracle.save()?;
println!();
println!("✓ Oracle patterns saved");
}
#[cfg(not(feature = "citl"))]
{
let _ = from;
return Err(anyhow::anyhow!(
"Pattern import requires the 'citl' feature.\n\nTry: cargo build -p decy --features citl"
));
}
#[allow(unreachable_code)]
Ok(())
}
#[cfg(feature = "oracle")]
fn handle_oracle_stats(format: String) -> Result<()> {
use decy_oracle::{DecyOracle, OracleConfig};
let config = OracleConfig::default();
let oracle = DecyOracle::new(config)?;
let metrics = oracle.metrics();
match format.to_lowercase().as_str() {
"json" => {
println!("{}", metrics.to_json());
}
"markdown" | "md" => {
use decy_oracle::{CIReport, CIThresholds};
let report =
CIReport::from_metrics(metrics.clone(), CIThresholds::default());
println!("{}", report.to_markdown());
}
"prometheus" | "prom" => {
println!("{}", metrics.to_prometheus());
}
_ => {
println!("=== Oracle Statistics ===");
println!("Pattern count: {}", oracle.pattern_count());
println!("Total queries: {}", metrics.queries);
println!("Cache hits: {}", metrics.hits);
println!("Cache misses: {}", metrics.misses);
println!("Fixes applied: {}", metrics.fixes_applied);
println!("Fixes verified: {}", metrics.fixes_verified);
if metrics.queries > 0 {
let hit_rate = (metrics.hits as f64 / metrics.queries as f64) * 100.0;
println!("Hit rate: {:.1}%", hit_rate);
}
}
}
Ok(())
}
#[cfg(feature = "oracle")]
fn handle_oracle_retire(dry_run: bool, archive_path: Option<PathBuf>) -> Result<()> {
use decy_oracle::{DecyOracle, OracleConfig, PatternRetirementPolicy};
let config = OracleConfig::default();
let oracle = DecyOracle::new(config)?;
let policy = PatternRetirementPolicy::new();
println!("=== Pattern Retirement Analysis ===");
println!("Pattern count: {}", oracle.pattern_count());
if dry_run {
println!();
println!("DRY RUN MODE - No patterns will be retired");
println!();
println!("Retirement policy thresholds:");
println!(" Min uses: {}", policy.config().min_usage_threshold);
println!(
" Min success rate: {:.1}%",
policy.config().min_success_rate * 100.0
);
println!(" Window: {} days", policy.config().evaluation_window_days);
} else {
println!();
if let Some(ref archive) = archive_path {
println!("Archive path: {}", archive.display());
}
println!();
println!("Note: Full retirement requires pattern usage statistics.");
println!("Run with --dry-run to see policy thresholds.");
}
Ok(())
}
#[cfg(feature = "oracle")]
fn handle_oracle_validate(corpus: PathBuf) -> Result<()> {
use decy_oracle::diversity::{analyze_corpus, DiversityValidation};
use decy_oracle::{DecyOracle, OracleConfig};
if !corpus.exists() {
anyhow::bail!(
"Corpus directory not found: {}\n\nTry: Verify the path to the corpus",
corpus.display()
);
}
println!("Validating oracle on corpus: {}", corpus.display());
println!();
let histogram = analyze_corpus(&corpus)
.map_err(|e| anyhow::anyhow!("Failed to analyze corpus: {}", e))?;
println!("=== Corpus Diversity Analysis ===");
println!("Files: {}", histogram.total_files);
println!("Lines of code: {}", histogram.total_loc);
println!();
if !histogram.construct_coverage.is_empty() {
println!("C Construct Coverage:");
for (construct, count) in &histogram.construct_coverage {
println!(" {:?}: {}", construct, count);
}
println!();
}
let c_files: Vec<_> = walkdir::WalkDir::new(&corpus)
.into_iter()
.filter_map(|e| e.ok())
.filter(|e| e.path().extension().and_then(|s| s.to_str()) == Some("c"))
.collect();
println!("Found {} C files in corpus", c_files.len());
let config = OracleConfig::default();
let mut oracle = DecyOracle::new(config)?;
let mut error_histogram = decy_oracle::diversity::ErrorHistogram::new();
let mut transpile_success = 0;
let mut transpile_failed = 0;
for entry in &c_files {
let path = entry.path();
let c_code = match std::fs::read_to_string(path) {
Ok(c) => c,
Err(_) => continue,
};
match decy_core::transpile(&c_code) {
Ok(_) => transpile_success += 1,
Err(e) => {
transpile_failed += 1;
let error_str = e.to_string();
if let Some(code_start) = error_str.find("E0") {
let code: String =
error_str[code_start..].chars().take(5).collect();
error_histogram.record_error(&code);
oracle
.record_miss(&decy_oracle::RustcError::new(&code, &error_str));
} else {
error_histogram.record_error("E0000");
oracle.record_miss(&decy_oracle::RustcError::new(
"E0000",
"transpilation failed",
));
}
}
}
}
println!();
println!("=== Validation Results ===");
println!("Files processed: {}", c_files.len());
println!("Transpile success: {}", transpile_success);
println!("Transpile failed: {}", transpile_failed);
if !c_files.is_empty() {
let success_rate = (transpile_success as f64 / c_files.len() as f64) * 100.0;
println!("Success rate: {:.1}%", success_rate);
}
if !error_histogram.by_error_code.is_empty() {
println!();
println!("Error Distribution:");
for (code, count) in &error_histogram.by_error_code {
let category = decy_oracle::diversity::categorize_error(code);
println!(" {}: {} ({:?})", code, count, category);
}
}
println!();
println!("Oracle metrics after validation:");
let metrics = oracle.metrics();
println!(" Queries: {}", metrics.queries);
println!(" Misses: {}", metrics.misses);
let validation = DiversityValidation::new(error_histogram);
if validation.passed {
println!();
println!("✅ Corpus diversity validation: PASSED");
}
Ok(())
}
#[cfg(feature = "oracle")]
fn handle_oracle_export(output: PathBuf, format: String, with_card: bool) -> Result<()> {
use decy_oracle::dataset::{generate_dataset_card, DatasetExporter};
println!("=== Oracle Dataset Export ===");
println!();
let exporter = DatasetExporter::new();
let stats = exporter.stats();
println!("Patterns to export: {}", exporter.len());
println!("Verified: {}", stats.verified);
println!();
let count = match format.to_lowercase().as_str() {
"jsonl" => {
println!("Exporting to JSONL format...");
exporter.export_jsonl(&output)?
}
"chatml" => {
println!("Exporting to ChatML format...");
exporter.export_chatml(&output)?
}
"alpaca" => {
println!("Exporting to Alpaca format...");
exporter.export_alpaca(&output)?
}
"parquet" => {
println!("Exporting to Parquet format...");
exporter.export_parquet(&output)?
}
_ => {
anyhow::bail!(
"Unknown export format: {}\n\nSupported formats: jsonl, chatml, alpaca, parquet",
format
);
}
};
println!("✓ Exported {} patterns to {}", count, output.display());
if with_card {
let card_path = output.with_file_name("README.md");
let card = generate_dataset_card(&stats);
std::fs::write(&card_path, &card)?;
println!("✓ Generated dataset card: {}", card_path.display());
}
println!();
println!("Statistics:");
println!("{}", stats.to_markdown());
Ok(())
}
#[cfg(feature = "oracle")]
fn handle_oracle_train(corpus: PathBuf, tier: String, dry_run: bool) -> Result<()> {
if !corpus.exists() {
anyhow::bail!(
"Corpus directory not found: {}\n\nTry: Verify the path to the corpus",
corpus.display()
);
}
let tier_upper = tier.to_uppercase();
if !["P0", "P1", "P2"].contains(&tier_upper.as_str()) {
anyhow::bail!("Invalid tier: {}\n\nSupported tiers: P0, P1, P2", tier);
}
let c_files: Vec<_> = walkdir::WalkDir::new(&corpus)
.into_iter()
.filter_map(|e| e.ok())
.filter(|e| e.path().extension().map(|ext| ext == "c").unwrap_or(false))
.collect();
if c_files.is_empty() {
anyhow::bail!(
"No C files found in corpus: {}\n\nTry: Add .c files to the corpus directory",
corpus.display()
);
}
println!("=== Oracle CITL Training ===");
println!();
println!("Corpus: {}", corpus.display());
println!("Tier: {}", tier_upper);
println!("Files: {}", c_files.len());
if dry_run {
println!("Mode: DRY RUN (no patterns will be saved)");
}
println!();
let mut files_processed = 0;
let mut total_errors = 0;
let mut patterns_captured = 0;
let context = decy_core::ProjectContext::default();
for entry in &c_files {
let c_path = entry.path();
println!("Training on: {}", c_path.display());
let transpiled = match decy_core::transpile_file(c_path, &context) {
Ok(t) => t,
Err(e) => {
println!(" ⚠ Transpile failed: {}", e);
continue;
}
};
let temp_dir = std::env::temp_dir();
let rust_path = temp_dir.join(format!("decy_train_{}.rs", files_processed));
std::fs::write(&rust_path, &transpiled.rust_code)?;
let output = std::process::Command::new("rustc")
.arg("--error-format=json")
.arg("--emit=metadata")
.arg("-o")
.arg("/dev/null")
.arg(&rust_path)
.output()?;
let stderr = String::from_utf8_lossy(&output.stderr);
let errors: Vec<&str> =
stderr.lines().filter(|l| l.contains("\"level\":\"error\"")).collect();
let error_count = errors.len();
total_errors += error_count;
if error_count > 0 {
println!(" Errors: {}", error_count);
for error_line in &errors {
if let Some(code_start) = error_line.find("\"code\":{\"code\":\"") {
let code_substr = &error_line[code_start + 16..];
if let Some(code_end) = code_substr.find('"') {
let error_code = &code_substr[..code_end];
println!(" - {}", error_code);
if !dry_run {
patterns_captured += 1;
}
}
}
}
} else {
println!(" ✓ No errors");
}
files_processed += 1;
}
println!();
println!("=== Training Summary ===");
println!("Files processed: {}", files_processed);
println!("Total errors: {}", total_errors);
println!("Patterns captured: {}", patterns_captured);
if dry_run {
println!();
println!("DRY RUN - No patterns were saved");
} else if patterns_captured > 0 {
println!();
println!("✓ Training complete");
}
Ok(())
}
#[cfg(feature = "oracle")]
fn handle_oracle_generate_traces(
corpus: PathBuf,
output: PathBuf,
tier: String,
dry_run: bool,
) -> Result<()> {
use decy_oracle::golden_trace::{GoldenTrace, GoldenTraceDataset, TraceTier};
use decy_oracle::trace_verifier::TraceVerifier;
if !corpus.exists() {
anyhow::bail!(
"Corpus directory not found: {}\n\nTry: Verify the path to the corpus",
corpus.display()
);
}
let tier_upper = tier.to_uppercase();
let trace_tier = match tier_upper.as_str() {
"P0" => TraceTier::P0,
"P1" => TraceTier::P1,
"P2" => TraceTier::P2,
_ => anyhow::bail!("Invalid tier: {}\n\nSupported tiers: P0, P1, P2", tier),
};
let c_files: Vec<_> = walkdir::WalkDir::new(&corpus)
.into_iter()
.filter_map(|e| e.ok())
.filter(|e| e.path().extension().map(|ext| ext == "c").unwrap_or(false))
.collect();
if c_files.is_empty() {
anyhow::bail!(
"No C files found in corpus: {}\n\nTry: Add .c files to the corpus directory",
corpus.display()
);
}
println!("=== Golden Trace Generation ===");
println!();
println!("Corpus: {}", corpus.display());
println!("Output: {}", output.display());
println!("Tier: {}", trace_tier);
println!("Files: {}", c_files.len());
if dry_run {
println!("Mode: DRY RUN (no output file will be written)");
}
println!();
let mut files_processed = 0;
let mut traces_generated = 0;
let mut traces_verified = 0;
let mut traces_failed = 0;
let mut traces_skipped = 0;
let mut dataset = GoldenTraceDataset::new();
let mut verifier = TraceVerifier::new();
let context = decy_core::ProjectContext::default();
for entry in &c_files {
let c_path = entry.path();
let filename = c_path.file_name().and_then(|s| s.to_str()).unwrap_or("unknown");
println!("Processing: {}", c_path.display());
let c_code = match std::fs::read_to_string(c_path) {
Ok(code) => code,
Err(e) => {
println!(" ⚠ Failed to read: {}", e);
traces_skipped += 1;
continue;
}
};
let transpiled = match decy_core::transpile_file(c_path, &context) {
Ok(t) => t,
Err(e) => {
println!(" ⚠ Transpile failed: {}", e);
traces_failed += 1;
continue;
}
};
let trace = GoldenTrace::new(
c_code.clone(),
transpiled.rust_code.clone(),
trace_tier,
filename,
);
let result = verifier.verify_trace(&trace);
if result.passed {
println!(" ✓ Verified - generating trace");
traces_verified += 1;
let explanation =
generate_safety_explanation(&c_code, &transpiled.rust_code, trace_tier);
let trace_with_explanation = trace.with_safety_explanation(&explanation);
dataset.add_trace(trace_with_explanation);
traces_generated += 1;
} else {
println!(" ✗ Verification failed: {:?}", result.errors);
traces_failed += 1;
}
files_processed += 1;
}
println!();
println!("=== Generation Summary ===");
println!("Files processed: {}", files_processed);
println!("Traces generated: {}", traces_generated);
println!("Traces verified: {}", traces_verified);
println!("Traces failed: {}", traces_failed);
println!("Traces skipped: {}", traces_skipped);
if dry_run {
println!();
println!("DRY RUN - Would generate {} traces", traces_generated);
println!("Would write to: {}", output.display());
} else if traces_generated > 0 {
dataset.export_jsonl(&output)?;
println!();
println!(
"✓ Exported {} Golden Traces to {}",
traces_generated,
output.display()
);
} else {
println!();
println!("⚠ No traces generated - check corpus files");
}
Ok(())
}
#[cfg(feature = "oracle")]
fn handle_oracle_query(
error: String,
context: Option<String>,
format: String,
) -> Result<()> {
use decy_oracle::bootstrap::get_bootstrap_patterns;
if !error.starts_with('E') || error.len() != 5 {
anyhow::bail!(
"Invalid error code format: {}\n\nExpected format: EXXXX (e.g., E0308, E0382)",
error
);
}
let patterns = get_bootstrap_patterns();
let matching: Vec<_> = patterns.iter().filter(|p| p.error_code == error).collect();
if format.to_lowercase() == "json" {
let json_patterns: Vec<_> = matching
.iter()
.map(|p| {
serde_json::json!({
"error_code": p.error_code,
"decision": p.decision,
"description": p.description,
"fix_diff": p.fix_diff,
})
})
.collect();
println!(
"{}",
serde_json::to_string_pretty(&serde_json::json!({
"error_code": error,
"context": context,
"patterns_found": matching.len(),
"patterns": json_patterns,
}))?
);
} else {
println!("=== Oracle Query: {} ===", error);
println!();
if let Some(ref ctx) = context {
println!("Context: {}", ctx);
println!();
}
if matching.is_empty() {
println!("No patterns found for error code {}", error);
println!();
println!("Tip: Use 'decy oracle bootstrap' to load seed patterns");
} else {
println!("Found {} pattern(s) for {}:", matching.len(), error);
println!();
for (i, pattern) in matching.iter().enumerate() {
println!("--- Pattern {} ---", i + 1);
println!("Decision: {}", pattern.decision);
println!("Description: {}", pattern.description);
println!();
println!("Fix:");
for line in pattern.fix_diff.lines() {
println!(" {}", line);
}
println!();
}
}
}
Ok(())
}
#[cfg(feature = "oracle")]
fn generate_safety_explanation(
c_code: &str,
rust_code: &str,
tier: decy_oracle::golden_trace::TraceTier,
) -> String {
use decy_oracle::golden_trace::TraceTier;
let mut explanation = String::new();
explanation.push_str("## Safety Analysis\n\n");
match tier {
TraceTier::P0 => {
explanation.push_str("### Tier P0: Simple Pattern Transformation\n\n");
explanation.push_str("This is a straightforward type transformation. ");
explanation.push_str(
"The C code uses primitive types that map directly to Rust's safe type system.\n\n",
);
}
TraceTier::P1 => {
explanation.push_str("### Tier P1: I/O and Pointer Transformation\n\n");
explanation.push_str("This transformation involves pointer handling. ");
}
TraceTier::P2 => {
explanation.push_str("### Tier P2: Complex Memory Management\n\n");
explanation.push_str("This transformation involves complex memory patterns. ");
}
}
if c_code.contains("malloc") || c_code.contains("free") {
explanation.push_str("**Memory Management**: ");
explanation.push_str("The C code uses manual memory allocation (malloc/free). ");
explanation.push_str(
"The Rust code uses RAII patterns (Box, Vec) for automatic memory management, ",
);
explanation
.push_str("eliminating potential use-after-free and memory leak vulnerabilities.\n\n");
}
if c_code.contains("*") && (c_code.contains("int *") || c_code.contains("char *")) {
explanation.push_str("**Pointer Safety**: ");
explanation.push_str("The C code uses raw pointers. ");
explanation.push_str(
"The Rust code converts these to references (&T, &mut T) with borrow checking, ",
);
explanation.push_str("ensuring memory safety at compile time.\n\n");
}
if c_code.contains("NULL") {
explanation.push_str("**Null Safety**: ");
explanation.push_str("The C code checks for NULL. ");
explanation
.push_str("The Rust code uses Option<T> to encode nullability in the type system, ");
explanation.push_str("preventing null pointer dereferences.\n\n");
}
if rust_code.contains("unsafe") {
explanation.push_str("**Unsafe Blocks**: ");
explanation
.push_str("Some unsafe operations remain where Rust cannot statically verify safety. ");
explanation
.push_str("These are minimized and isolated with documented safety invariants.\n\n");
} else {
explanation.push_str("**100% Safe**: ");
explanation.push_str("The generated Rust code contains no unsafe blocks, ");
explanation.push_str("providing compile-time memory safety guarantees.\n\n");
}
explanation
}