1use std::io::Cursor;
4
5use clap::Parser;
6use itertools::Itertools as _;
7use rust2go_common::common::RawRsFile;
8
9#[derive(Parser, Debug, Default, Clone)]
10#[command(author, version, about, long_about = None)]
11pub struct Args {
12 #[arg(short, long)]
14 pub src: String,
15
16 #[arg(short, long)]
18 pub dst: String,
19
20 #[arg(long, default_value = "false")]
22 pub without_main: bool,
23
24 #[arg(long, default_value = "false")]
26 pub go118: bool,
27
28 #[arg(long, default_value = "false")]
30 pub no_fmt: bool,
31}
32
33pub fn generate(args: &Args) {
34 let file_content = std::fs::read_to_string(&args.src).expect("Unable to read file");
36 let raw_file = RawRsFile::new(file_content);
37
38 let (name_mapping, ref_content) = raw_file
40 .convert_structs_to_ref()
41 .expect("Unable to convert to ref");
42 std::fs::write(&args.dst, ref_content.to_string()).expect("Unable to write file");
43
44 let mut cbuilder = cbindgen::Builder::new()
46 .with_language(cbindgen::Language::C)
47 .with_src(&args.dst)
48 .with_header("// Generated by rust2go. Please DO NOT edit this C part manually.");
49 for name in name_mapping.values().map(|n| n.to_string()).sorted() {
50 cbuilder = cbuilder.include_item(name);
51 }
52 let mut output = Vec::<u8>::new();
53 cbuilder
54 .generate()
55 .expect("Unable to generate bindings")
56 .write(Cursor::new(&mut output));
57
58 let mut importc = String::from_utf8(output).expect("Unable to convert to string");
60
61 let r2g_traits = raw_file.convert_r2g_trait().unwrap();
62 let g2r_traits = raw_file.convert_g2r_trait().unwrap();
63 macro_rules! r2g_any {
64 ($f: expr) => {
65 r2g_traits.iter().any(|t| t.fns().iter().any($f))
66 };
67 }
68 macro_rules! g2r_any {
69 ($f: expr) => {
70 g2r_traits.iter().any(|t| t.fns().iter().any($f))
71 };
72 }
73 macro_rules! or_empty {
74 ($flag: expr, $content: expr) => {
75 if $flag {
76 $content
77 } else {
78 ""
79 }
80 };
81 }
82 let use_shm = r2g_any!(|f| f.mem_call_id().is_some());
83 let use_runtime =
84 r2g_any!(|f| f.mem_call_id().is_none()) || g2r_traits.iter().any(|t| !t.fns().is_empty());
85 let use_cgocall =
86 r2g_any!(|f| f.mem_call_id().is_none() && f.cgo_callback()) || g2r_any!(|f| f.cgo_call());
87 let use_asmcall =
88 r2g_any!(|f| f.mem_call_id().is_none() && !f.cgo_callback() && f.ret().is_some())
89 || g2r_any!(|f| !f.cgo_call());
90 if use_shm {
91 importc.push_str(RawRsFile::go_shm_include());
92 }
93 if g2r_traits.iter().any(|t| t.has_ret()) {
94 importc.push_str(RawRsFile::go_internal_drop());
95 }
96 g2r_traits.iter().for_each(|t| {
97 importc.push_str(&t.to_importc());
98 });
99
100 let import_shm = or_empty!(
101 use_shm,
102 "mem_ring \"github.com/ihciah/rust2go/mem-ring\"\n\"github.com/panjf2000/ants/v2\"\n"
103 );
104 let import_runtime = or_empty!(use_runtime, "\"runtime\"\n");
105 let import_cgocall = or_empty!(use_cgocall, "\"github.com/ihciah/rust2go/cgocall\"\n");
106 let import_asmcall = or_empty!(use_asmcall, "\"github.com/ihciah/rust2go/asmcall\"\n");
107 let import_118 = or_empty!(args.go118, "\"reflect\"\n");
108
109 let mut go_content = format!(
110 "package main\n\n/*\n{importc}*/\nimport \"C\"\nimport (\n\"unsafe\"\n{import_runtime}{import_118}{import_shm}\n{import_cgocall}{import_asmcall})\n"
111 );
112 let levels = raw_file.convert_structs_levels().unwrap();
113 r2g_traits.iter().for_each(|t| {
114 go_content.push_str(&t.generate_go_interface());
115 go_content.push_str(&t.generate_go_exports(&levels));
116 });
117 go_content.push_str(
118 &raw_file
119 .convert_structs_to_go(&levels, args.go118)
120 .expect("Unable to generate go structs"),
121 );
122 if use_shm {
123 go_content.push_str(RawRsFile::go_shm_ring_init());
124 }
125 g2r_traits.iter().for_each(|t| {
126 go_content.push_str(&t.to_go(&levels));
127 });
128 if !args.without_main {
129 go_content.push_str("func main() {}\n");
130 }
131
132 std::fs::write(&args.dst, go_content).expect("Unable to write file");
133
134 if !args.no_fmt {
135 std::process::Command::new("go")
136 .arg("fmt")
137 .arg(&args.dst)
138 .status()
139 .unwrap();
140 }
141}