autd3capi_wrapper_generator/
lib.rs1mod parse;
15mod python;
16mod traits;
17mod types;
18
19use std::path::Path;
20
21use anyhow::Result;
22
23use cargo_metadata::MetadataCommand;
24use convert_case::{Case, Casing};
25use parse::{parse_const, parse_enum, parse_func, parse_struct};
26use python::PythonGenerator;
27use traits::Generator;
28
29fn gen<G: Generator, P1: AsRef<Path>, P2: AsRef<Path>>(
30 path: P1,
31 crate_path: P2,
32 use_single: bool,
33) -> Result<()> {
34 std::fs::create_dir_all(path.as_ref())?;
35
36 let metadata = MetadataCommand::new()
37 .manifest_path(crate_path.as_ref().join("Cargo.toml"))
38 .exec()?;
39
40 let crate_name = metadata.root_package().unwrap().name.as_str();
41
42 glob::glob(&format!(
43 "{}/**/*.rs",
44 crate_path.as_ref().join("src").display()
45 ))?
46 .try_fold(G::new(), |acc, path| -> Result<_> {
47 let path = path?;
48 Ok(acc
49 .register_func(parse_func(&path, use_single)?)
50 .register_const(parse_const(&path, use_single)?)
51 .register_enum(parse_enum(&path, use_single)?)
52 .register_struct(parse_struct(&path, use_single)?))
53 })?
54 .write(path, crate_name)
55}
56
57fn generate_cs<P1: AsRef<Path>, P2: AsRef<Path>>(
58 path: P1,
59 crate_path: P2,
60 use_single: bool,
61) -> Result<()> {
62 let sub_abbr =
63 |str: String| -> String { str.replace("Twincat", "TwinCAT").replace("Soem", "SOEM") };
64
65 let to_pascal = |name: &str| -> String {
66 let res = name.to_case(Case::Pascal);
67 sub_abbr(res)
68 };
69
70 let to_class_name = |name: &str| {
71 if name.split('-').count() == 1 {
72 return "Base".to_string();
73 }
74 to_pascal(&name.replace("autd3capi-", ""))
75 };
76
77 let crate_name = crate_path.as_ref().file_name().unwrap().to_str().unwrap();
78 let out_file = Path::new(path.as_ref()).join(format!("{}.cs", to_class_name(crate_name)));
79 let dll_name = crate_name.replace('-', "_");
80 let class_name = to_class_name(crate_name);
81
82 glob::glob(&format!(
83 "{}/**/*.rs",
84 crate_path.as_ref().join("src").display()
85 ))?
86 .try_fold(csbindgen::Builder::default(), |acc, path| -> Result<_> {
87 let path = path?;
88 Ok(acc.input_extern_file(path))
89 })?
90 .csharp_dll_name(dll_name)
91 .csharp_class_name(format!("NativeMethods{}", class_name))
92 .csharp_namespace("AUTD3Sharp.NativeMethods")
93 .csharp_generate_const_filter(|_| true)
94 .csharp_class_accessibility("public")
95 .generate_csharp_file(&out_file)
96 .map_err(|_| anyhow::anyhow!("failed to generate cs wrapper"))?;
97
98 let content = std::fs::read_to_string(&out_file)?;
99 let content = content.replace("@float", if use_single { "float" } else { "double" });
100 let content = content.replace("ConstPtr", "IntPtr");
101 let content = content.replace("void*", "IntPtr");
102 let content = content.replace("SamplingConfiguration", "SamplingConfigurationRaw");
103
104 let content = content.replace("Drive*", "DriveRaw*");
105
106 let content = if use_single {
107 let re = regex::Regex::new(r"public const float (.*) = (.*);").unwrap();
108 re.replace_all(&content, "public const float $1 = ${2}f;")
109 .to_string()
110 } else {
111 content
112 };
113
114 std::fs::write(&out_file, content)?;
115
116 Ok(())
117}
118
119pub fn gen_c<P1: AsRef<Path>, P2: AsRef<Path>>(crate_path: P1, dest_dir: P2) -> Result<()> {
120 let out_file = dest_dir.as_ref().join(format!(
121 "{}.h",
122 crate_path.as_ref().file_name().unwrap().to_str().unwrap()
123 ));
124 let mut config = cbindgen::Config::default();
125 config.language = cbindgen::Language::Cxx;
126 config.pragma_once = true;
127 config.autogen_warning = Some(
128 "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */"
129 .to_string(),
130 );
131 config.namespace = Some("autd3::internal::native_methods".to_string());
132 config.no_includes = true;
133 config.sys_includes = vec!["cstdint".to_string()];
134 config.sort_by = cbindgen::SortKey::None;
135 config.usize_is_size_t = true;
136 config.export = cbindgen::ExportConfig {
137 include: vec![
138 "TimerStrategy".to_string(),
139 "GainSTMMode".to_string(),
140 "ControllerPtr".to_string(),
141 "EmissionConstraintPtr".to_string(),
142 "FirmwareInfoListPtr".to_string(),
143 "GroupKVMapPtr".to_string(),
144 "CachePtr".to_string(),
145 "DevicePtr".to_string(),
146 "TransducerPtr".to_string(),
147 "GeometryPtr".to_string(),
148 "ModulationPtr".to_string(),
149 "GainPtr".to_string(),
150 "LinkPtr".to_string(),
151 "DatagramPtr".to_string(),
152 "DatagramSpecialPtr".to_string(),
153 "STMPropsPtr".to_string(),
154 "BackendPtr".to_string(),
155 "GroupGainMapPtr".to_string(),
156 "GainCalcDrivesMapPtr".to_string(),
157 "LinkBuilderPtr".to_string(),
158 "ResultI32".to_string(),
159 "ResultModulation".to_string(),
160 "ResultBackend".to_string(),
161 "ResultController".to_string(),
162 "ResultGainCalcDrivesMap".to_string(),
163 "ResultDatagram".to_string(),
164 "Drive".to_string(),
165 ],
166 exclude: vec!["ConstPtr".to_string()],
167 rename: vec![
168 ("float".to_string(), "double".to_string()),
169 ("ConstPtr".to_string(), "void*".to_string()),
170 ]
171 .into_iter()
172 .collect(),
173 ..Default::default()
174 };
175 config.function = cbindgen::FunctionConfig {
176 sort_by: None,
177 must_use: Some("[[nodiscard]]".to_string()),
178 ..Default::default()
179 };
180 config.constant = cbindgen::ConstantConfig {
181 allow_static_const: false,
182 allow_constexpr: true,
183 sort_by: Some(cbindgen::SortKey::None),
184 };
185
186 cbindgen::Builder::new()
187 .with_crate(crate_path)
188 .with_config(config)
189 .generate()?
190 .write_to_file(out_file);
191
192 Ok(())
193}
194
195pub fn gen_py<P1: AsRef<Path>, P2: AsRef<Path>>(crate_path: P1, dest_dir: P2) -> Result<()> {
196 gen::<PythonGenerator, _, _>(dest_dir, crate_path, false)
197}
198
199pub fn gen_cs<P1: AsRef<Path>, P2: AsRef<Path>>(crate_path: P1, dest_dir: P2) -> Result<()> {
200 generate_cs(dest_dir, crate_path, false)
201}
202
203pub fn gen_unity<P1: AsRef<Path>, P2: AsRef<Path>>(crate_path: P1, dest_dir: P2) -> Result<()> {
204 generate_cs(dest_dir, crate_path, true)
205}
206
207pub fn generate<P: AsRef<Path>>(crate_path: P) -> Result<()> {
208 gen_py(&crate_path, "../../python/pyautd3/native_methods")?;
209
210 gen_c(
211 &crate_path,
212 "../../cpp/include/autd3/internal/native_methods",
213 )?;
214 gen_cs(&crate_path, "../../dotnet/cs/src/NativeMethods")?;
215 gen_unity(
216 &crate_path,
217 "../../dotnet/unity/Assets/Scripts/NativeMethods",
218 )?;
219
220 Ok(())
221}