#![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),
_ => {}
}
}
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());
}
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]); }
}
}
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]); }
}
}
}