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()) || g2r_any!(|f| !f.cgo_call());
89 if use_shm {
90 importc.push_str(RawRsFile::go_shm_include());
91 }
92 if g2r_traits.iter().any(|t| t.has_ret()) {
93 importc.push_str(RawRsFile::go_internal_drop());
94 }
95 g2r_traits.iter().for_each(|t| {
96 importc.push_str(&t.to_importc());
97 });
98
99 let import_shm = or_empty!(
100 use_shm,
101 "mem_ring \"github.com/ihciah/rust2go/mem-ring\"\n\"github.com/panjf2000/ants/v2\"\n"
102 );
103 let import_runtime = or_empty!(use_runtime, "\"runtime\"\n");
104 let import_cgocall = or_empty!(use_cgocall, "\"github.com/ihciah/rust2go/cgocall\"\n");
105 let import_asmcall = or_empty!(use_asmcall, "\"github.com/ihciah/rust2go/asmcall\"\n");
106 let import_118 = or_empty!(args.go118, "\"reflect\"\n");
107
108 let mut go_content = format!(
109 "package main\n\n/*\n{importc}*/\nimport \"C\"\nimport (\n\"unsafe\"\n{import_runtime}{import_118}{import_shm}\n{import_cgocall}{import_asmcall})\n"
110 );
111 let levels = raw_file.convert_structs_levels().unwrap();
112 r2g_traits.iter().for_each(|t| {
113 go_content.push_str(&t.generate_go_interface());
114 go_content.push_str(&t.generate_go_exports(&levels));
115 });
116 go_content.push_str(
117 &raw_file
118 .convert_structs_to_go(&levels, args.go118)
119 .expect("Unable to generate go structs"),
120 );
121 if use_shm {
122 go_content.push_str(RawRsFile::go_shm_ring_init());
123 }
124 g2r_traits.iter().for_each(|t| {
125 go_content.push_str(&t.to_go(&levels));
126 });
127 if !args.without_main {
128 go_content.push_str("func main() {}\n");
129 }
130
131 std::fs::write(&args.dst, go_content).expect("Unable to write file");
132
133 if !args.no_fmt {
134 std::process::Command::new("go")
135 .arg("fmt")
136 .arg(&args.dst)
137 .status()
138 .unwrap();
139 }
140}