ccarp 0.1.2

(trans)Compile C And Rust Partially
Documentation
//! CCARP - (trans)Compile C And Rust Partially
//! 
//! CCARP is a partial transpiler which can translate some C programs into Rust.
//! 
//! Command line utility:
//! - `ccarp --help` will show how to use this program
//! - `ccarp --version` will reveal its version
//! - `ccarp file [PATH]` will translate a single C file to Rust
//! - `ccarp [PATH]` will translate an entire C project to Rust

#![warn(clippy::pedantic,clippy::nursery,clippy::complexity,clippy::perf,clippy::correctness,clippy::all)]
#![warn(clippy::cognitive_complexity,clippy::large_const_arrays)]
#![warn(clippy::style,clippy::suspicious,large_assignments,rustdoc::all)]
#![allow(clippy::must_use_candidate,clippy::wildcard_imports,clippy::enum_glob_use)]

mod ccarp;
mod ccarp_c;
mod ccarp_rust;

use std::env::args;

use ccarp::translator::{translate_project, translate_single_file, CompilerFlags};

fn print_help() {
    println!("CCARP - (trans)Compile C And Rust Partially");
    println!("Version {}",env!("CARGO_PKG_VERSION"));
    println!();
    println!("Usage:\tccarp file [PATH] [OPTIONS] - translate a single file (new project will be under './autogenerated')");
    println!("\tccarp [PATH] [OPTIONS] - translate an entire project (new project will be under './autogenerated')");
    println!();
    println!("Options:");
    println!("  -h, --help\t\t\tPrint help (this screen)");
    println!("  -v, --version\t\t\tPrint version information and exit");
    println!();
    println!("  -n, --name [GENERATED_NAME]\tSet name of generated project to [GENERATED_NAME] (instead of autogenerated)");
    println!();
    println!("      --ref_instead_of_point\tUse references instead of pointers in translation (replace pointers with references)");
    println!("      --point_instead_of_ref\tUse pointers instead of references in translation (replace references with pointers)");
    println!();
    println!("      --thin_point\t\tUse thin pointers in translation (a.k.a. pointers that refer to a single element and not an array)");
    println!("      --array_point\t\tUse array pointers in translation (a.k.a. pointers that refer to an array)");
    println!();
    println!("      --thin_ref\t\tUse thin references in translation (a.k.a. references that refer to a single element and not an array)");
    println!("      --array_ref\t\tUse array references in translation (a.k.a. references that refer to an array)");
    println!();
    println!("      --array_as_point\t\tTranslate arrays inside functions and structures as pointers (thin or array pointers based on flags)");
    println!("      --array_as_ref\t\tTranslate arrays inside functions and structures as references (thin or array references based on flags)");
    println!("      --array_as_generic\tTranslate arrays inside functions and structures as generic arrays (e.g. [i32;N])");
    println!();
    println!("      --nohelper\t\tDo not add the helper crate as a dependency (don't run 'cargo add ccarp_helper')");
    println!("      --unsafe_fix\t\tTry to fix generated code without paying attention to git (runs 'cargo fix --broken-code --allow-dirty --allow-no-vcs --allow-staged')");
    println!("      --nofix_broken\t\tDo not fix broken code in generated files (don't run 'cargo fix --broken-code --allow-dirty')");
    println!("      --nofix_warnings\t\tDo not fix warnings in generated files (don't run 'cargo fix --allow-dirty')");
    println!("      --noformat\t\tDo not format generated code (don't run 'cargo fmt')");
    println!();
    println!("  -p, --pedantic\t\tTry to generate code which is closer to Rust (it might not work, but it should be easier to refactor)");
    println!("  -m, --make_it_work\t\tTry to make code compile (this will make the code harder to read, but it has the best possibilities of working)");
    println!("  -r, --raw\t\t\tGet the raw output of the program (without formatting or error corrections)");
    println!();
    println!("Notes:");
    println!("\tSome options are mutually exclusive (e.g. '--ref_instead_of_point' and '--point_instead_of_ref'),");
    println!("\t\tso if multiple contradictory options are specified, the last one will take effect");
    println!("\tDefault options\t= --array_point --array_ref --array_as_ref");
    println!("\t--pedantic\t= --ref_instead_of_point --array_point --array_ref --array_as_generic");
    println!("\t--make_it_work\t= --point_instead_of_ref --thin_point --thin_ref --array_as_point --unsafe_fix");
    println!("\t--raw\t\t= --array_point --array_ref --array_as_ref --nofix_broken --nofix_warnings");
}

fn apply_flag(flags: &mut CompilerFlags, flag: &str) {
    match flag {
        "--ref_instead_of_point" => flags.set_flags(CompilerFlags::UseRefInsteadOfPoint),
        "--point_instead_of_ref" => flags.set_flags(CompilerFlags::UsePointInsteadOfRef),
        "--thin_point" => flags.set_flags(CompilerFlags::ThinPoint),
        "--array_point" => flags.set_flags(CompilerFlags::ArrayPoint),
        "--thin_ref" => flags.set_flags(CompilerFlags::ThinRef),
        "--array_ref" => flags.set_flags(CompilerFlags::ArrayRef),
        "--array_as_point" => flags.set_flags(CompilerFlags::ArrayAsPoint),
        "--array_as_ref" => flags.set_flags(CompilerFlags::ArrayAsRef),
        "--array_as_generic" => flags.set_flags(CompilerFlags::ArrayAsGeneric),
        "--nohelper" => flags.set_flags(CompilerFlags::NoHelper),
        "--unsafe_fix" => flags.set_flags(CompilerFlags::UnsafeFix),
        "--nofix_broken" => flags.set_flags(CompilerFlags::NoFixBroken),
        "--nofix_warnings" => flags.set_flags(CompilerFlags::NoFixWarnings),
        "--noformat" => flags.set_flags(CompilerFlags::NoFormat),
        "-p"|"--pedantic" => flags.set_flags(CompilerFlags::Pedantic),
        "-m"|"--make_it_work" => flags.set_flags(CompilerFlags::MakeItWork),
        "-r"|"--raw" => flags.set_flags(CompilerFlags::Raw),
        _ => {}
    }
}

/// Function main
/// 
/// This is the entry point of our program
fn main() {
    let args: Vec<String> = args().collect();
    let mut name="autogenerated";
    if args.contains(&"--help".to_string()) || args.contains(&"-h".to_string()) || args.len()<2 { print_help(); }
    else if args.contains(&"--version".to_string()) || args.contains(&"-v".to_string()) || args.contains(&"-V".to_string()) {
        println!("{} {}",env!("CARGO_PKG_NAME"),env!("CARGO_PKG_VERSION"));
    }
    else {
        let mut flags=CompilerFlags::Default;
        for i in 0..args.len() {
            if matches!(args[i].as_str(),"-n"|"--name") {
                if i+1<args.len() { name=args[i+1].as_str(); }
                else { eprintln!("Expected name after option '{}'! Working with 'autogenerated'.",args[i]) }
            }
            apply_flag(&mut flags, args[i].as_str());
        }
        // Single file translation
        if args[1].as_str()=="file" {
            if args.len()==2 { eprintln!("Expected path after 'ccarp file'! (see 'ccarp --help')") }
            else {
                let res=translate_single_file(&args[2], name, flags);
                if let Err(e)=res { eprintln!("Translation failed! Error occurred during translation!\n{e}") }
                else { println!("Successfully translated file {}! Autogenerated project is './{name}'",args[2]); }
            }
        }
        // Whole project translation
        else {
            let res=translate_project(&args[1], name, flags);
            if let Err((file,e))=res { 
                if file.is_empty() { eprintln!("Translation failed! Error occurred during translation!\n{e}") }
                else { eprintln!("Translation failed! Error occurred during translation in file '{file}/!\n{e}") }
            }
            else { println!("Successfully translated project {0}! Autogenerated project is '{0}/{name}'",args[1]); }
        }
    }
}