1#![recursion_limit = "128"]
10
11macro_rules! parse_type {
12 ($($tt:tt)*) => {{
13 let ty: syn::Type = syn::parse_quote! { $($tt)* };
14 ty
15 }}
16}
17
18macro_rules! parse_spanned {
19 ($span:ident, $($tt:tt)*) => {{
20 let tt = quote::quote_spanned! { $span=> $($tt)* };
21 syn::parse2(tt)
22 }}
23}
24
25macro_rules! parse_type_spanned_checked {
26 ($span:ident, $($tt:tt)*) => {{
27 let ty: syn::Type = parse_spanned!($span, $($tt)*)
28 .unwrap_or_else(|err| {
29 panic!("Can not parse type {}: {}", stringify!($($tt)*), err);
30 });
31 ty
32 }}
33}
34
35mod code_parse;
36mod cpp;
37mod error;
38pub mod file_cache;
39mod java_jni;
40mod namegen;
41mod python;
42mod source_registry;
43mod str_replace;
44mod typemap;
45mod types;
46
47use std::{
48 env, io,
49 io::Write,
50 mem,
51 path::{Path, PathBuf},
52 process::{Command, Stdio},
53 str,
54 str::FromStr,
55 sync::Arc,
56};
57
58use log::debug;
59use proc_macro2::TokenStream;
60use strum::EnumIter;
61use syn::spanned::Spanned;
62
63use crate::{
64 error::{panic_on_parse_error, DiagnosticError, Result},
65 source_registry::{SourceId, SourceRegistry},
66 typemap::{ast::DisplayToTokens, TypeMap},
67 types::ItemToExpand,
68};
69
70pub(crate) static WRITE_TO_MEM_FAILED_MSG: &str = "Write to memory buffer failed, no free mem?";
71pub(crate) static SMART_PTR_COPY_TRAIT: &str = "SmartPtrCopy";
72
73pub fn target_pointer_width_from_env() -> Option<usize> {
76 env::var("CARGO_CFG_TARGET_POINTER_WIDTH")
77 .ok()
78 .map(|p_width| {
79 <usize>::from_str(&p_width)
80 .expect("Can not convert CARGO_CFG_TARGET_POINTER_WIDTH to usize")
81 })
82}
83
84pub enum LanguageConfig {
86 JavaConfig(JavaConfig),
87 CppConfig(CppConfig),
88 PythonConfig(PythonConfig),
89}
90
91#[derive(Debug)]
93pub struct JavaConfig {
94 output_dir: PathBuf,
95 package_name: String,
96 null_annotation_package: Option<String>,
97 optional_package: String,
98 reachability_fence: JavaReachabilityFence,
99}
100
101impl JavaConfig {
102 pub fn new(output_dir: PathBuf, package_name: String) -> JavaConfig {
107 JavaConfig {
108 output_dir,
109 package_name,
110 null_annotation_package: None,
111 optional_package: "java.util".to_string(),
112 reachability_fence: JavaReachabilityFence::GenerateFence(8),
113 }
114 }
115 #[deprecated(note = "Use use_null_annotation_from_package instead")]
120 pub fn use_null_annotation(mut self, import_annotation: String) -> JavaConfig {
121 let suffix = ".NonNull";
122 if !import_annotation.ends_with(suffix) {
123 panic!(
124 "import_annotation({}) should ends with {}",
125 import_annotation, suffix
126 );
127 }
128 let package = &import_annotation[0..import_annotation.len() - suffix.len()];
129 self.null_annotation_package = Some(package.into());
130 self
131 }
132 pub fn use_null_annotation_from_package(
138 mut self,
139 null_annotation_package: String,
140 ) -> JavaConfig {
141 self.null_annotation_package = Some(null_annotation_package);
142 self
143 }
144 pub fn use_optional_package(mut self, optional_package: String) -> JavaConfig {
147 self.optional_package = optional_package;
148 self
149 }
150 pub fn use_reachability_fence(
154 mut self,
155 reachability_fence: JavaReachabilityFence,
156 ) -> JavaConfig {
157 self.reachability_fence = reachability_fence;
158 self
159 }
160}
161
162#[derive(Debug, Clone, Copy)]
164pub enum JavaReachabilityFence {
165 Std,
167 GenerateFence(usize),
174}
175
176pub struct CppConfig {
178 output_dir: PathBuf,
179 namespace_name: String,
180 cpp_optional: CppOptional,
181 cpp_variant: CppVariant,
182 cpp_str_view: CppStrView,
183 separate_impl_headers: bool,
186}
187
188#[derive(Clone, Copy, EnumIter)]
190pub enum CppOptional {
191 Std17,
193 Boost,
195}
196
197impl From<CppOptional> for &'static str {
198 fn from(x: CppOptional) -> Self {
199 match x {
200 CppOptional::Std17 => "CppOptional::Std17",
201 CppOptional::Boost => "CppOptional::Boost",
202 }
203 }
204}
205
206#[derive(Clone, Copy, EnumIter)]
208pub enum CppVariant {
209 Std17,
211 Boost,
213}
214
215impl From<CppVariant> for &'static str {
216 fn from(x: CppVariant) -> Self {
217 match x {
218 CppVariant::Std17 => "CppVariant::Std17",
219 CppVariant::Boost => "CppVariant::Boost",
220 }
221 }
222}
223
224#[derive(Clone, Copy, EnumIter)]
226pub enum CppStrView {
227 Std17,
229 Boost,
231}
232
233impl From<CppStrView> for &'static str {
234 fn from(x: CppStrView) -> Self {
235 match x {
236 CppStrView::Std17 => "CppStrView::Std17",
237 CppStrView::Boost => "CppStrView::Boost",
238 }
239 }
240}
241
242impl CppConfig {
243 pub fn new(output_dir: PathBuf, namespace_name: String) -> CppConfig {
248 CppConfig {
249 output_dir,
250 namespace_name,
251 cpp_optional: CppOptional::Std17,
252 cpp_variant: CppVariant::Std17,
253 cpp_str_view: CppStrView::Std17,
254 separate_impl_headers: false,
255 }
256 }
257 pub fn cpp_optional(self, cpp_optional: CppOptional) -> CppConfig {
258 CppConfig {
259 cpp_optional,
260 ..self
261 }
262 }
263 pub fn cpp_variant(self, cpp_variant: CppVariant) -> CppConfig {
264 CppConfig {
265 cpp_variant,
266 ..self
267 }
268 }
269 pub fn cpp_str_view(self, cpp_str_view: CppStrView) -> CppConfig {
270 CppConfig {
271 cpp_str_view,
272 ..self
273 }
274 }
275 pub fn use_boost(self) -> CppConfig {
278 CppConfig {
279 cpp_variant: CppVariant::Boost,
280 cpp_optional: CppOptional::Boost,
281 cpp_str_view: CppStrView::Boost,
282 ..self
283 }
284 }
285 pub fn separate_impl_headers(self, separate_impl_headers: bool) -> CppConfig {
288 CppConfig {
289 separate_impl_headers,
290 ..self
291 }
292 }
293}
294
295pub struct PythonConfig {
297 module_name: String,
298}
299
300impl PythonConfig {
301 pub fn new(module_name: String) -> PythonConfig {
304 PythonConfig { module_name }
305 }
306}
307
308pub struct Generator {
312 init_done: bool,
313 config: LanguageConfig,
314 conv_map: TypeMap,
315 conv_map_source: Vec<SourceId>,
316 foreign_lang_helpers: Vec<SourceCode>,
317 pointer_target_width: usize,
318 src_reg: SourceRegistry,
319 rustfmt_bindings: bool,
320 remove_not_generated_files: bool,
321}
322
323struct SourceCode {
324 id_of_code: String,
325 code: String,
326}
327
328static FOREIGNER_CLASS: &str = "foreigner_class";
329static FOREIGN_CLASS: &str = "foreign_class";
330static FOREIGN_ENUM: &str = "foreign_enum";
331static FOREIGN_INTERFACE: &str = "foreign_interface";
332static FOREIGN_CALLBACK: &str = "foreign_callback";
333static FOREIGNER_CODE: &str = "foreigner_code";
334static FOREIGN_CODE: &str = "foreign_code";
335static FOREIGN_TYPEMAP: &str = "foreign_typemap";
336
337impl Generator {
338 pub fn new(config: LanguageConfig) -> Generator {
339 let pointer_target_width = target_pointer_width_from_env();
340 let mut conv_map_source = Vec::new();
341 let mut foreign_lang_helpers = Vec::new();
342 let mut src_reg = SourceRegistry::default();
343 match config {
344 LanguageConfig::JavaConfig(ref java_cfg) => {
345 conv_map_source.push(
346 src_reg.register(SourceCode {
347 id_of_code: "jni-include.rs".into(),
348 code: include_str!("java_jni/jni-include.rs")
349 .replace(
350 "java.util.Optional",
351 &format!("{}.Optional", java_cfg.optional_package),
352 )
353 .replace(
354 "java/util/Optional",
355 &format!(
356 "{}/Optional",
357 java_cfg.optional_package.replace('.', "/")
358 ),
359 ),
360 }),
361 );
362 }
363 LanguageConfig::CppConfig(..) => {
364 conv_map_source.push(src_reg.register(SourceCode {
365 id_of_code: "cpp-include.rs".into(),
366 code: include_str!("cpp/cpp-include.rs").into(),
367 }));
368 foreign_lang_helpers.push(SourceCode {
369 id_of_code: "rust_vec_impl.hpp".into(),
370 code: include_str!("cpp/rust_vec_impl.hpp").into(),
371 });
372 foreign_lang_helpers.push(SourceCode {
373 id_of_code: "rust_foreign_vec_impl.hpp".into(),
374 code: include_str!("cpp/rust_foreign_vec_impl.hpp").into(),
375 });
376 foreign_lang_helpers.push(SourceCode {
377 id_of_code: "rust_foreign_slice_iter.hpp".into(),
378 code: include_str!("cpp/rust_foreign_slice_iter.hpp").into(),
379 });
380 foreign_lang_helpers.push(SourceCode {
381 id_of_code: "rust_foreign_slice_impl.hpp".into(),
382 code: include_str!("cpp/rust_foreign_slice_impl.hpp").into(),
383 });
384 foreign_lang_helpers.push(SourceCode {
385 id_of_code: "rust_slice_tmpl.hpp".into(),
386 code: include_str!("cpp/rust_slice_tmpl.hpp").into(),
387 });
388 }
389 LanguageConfig::PythonConfig(..) => {
390 conv_map_source.push(src_reg.register(SourceCode {
391 id_of_code: "python-include.rs".into(),
392 code: include_str!("python/python-include.rs").into(),
393 }));
394 }
395 }
396 Generator {
397 init_done: false,
398 config,
399 conv_map: TypeMap::default(),
400 conv_map_source,
401 foreign_lang_helpers,
402 pointer_target_width: pointer_target_width.unwrap_or(0),
403 src_reg,
404 rustfmt_bindings: false,
405 remove_not_generated_files: false,
406 }
407 }
408
409 pub fn with_pointer_target_width(mut self, pointer_target_width: usize) -> Self {
412 self.pointer_target_width = pointer_target_width;
413 self
414 }
415
416 pub fn rustfmt_bindings(mut self, doit: bool) -> Self {
418 self.rustfmt_bindings = doit;
419 self
420 }
421
422 pub fn remove_not_generated_files_from_output_directory(mut self, doit: bool) -> Self {
426 self.remove_not_generated_files = doit;
427 self
428 }
429
430 pub fn merge_type_map(mut self, id_of_code: &str, code: &str) -> Generator {
432 self.conv_map_source.push(self.src_reg.register(SourceCode {
433 id_of_code: id_of_code.into(),
434 code: code.into(),
435 }));
436 self
437 }
438
439 pub fn expand<S, D>(mut self, crate_name: &str, src: S, dst: D)
444 where
445 S: AsRef<Path>,
446 D: AsRef<Path>,
447 {
448 let src_cnt = std::fs::read_to_string(src.as_ref()).unwrap_or_else(|err| {
449 panic!(
450 "Error during read for file {}: {}",
451 src.as_ref().display(),
452 err
453 )
454 });
455
456 let src_id = self.src_reg.register(SourceCode {
457 id_of_code: format!("{}: {}", crate_name, src.as_ref().display()),
458 code: src_cnt,
459 });
460
461 if let Err(err) = self.expand_str(src_id, dst) {
462 panic_on_parse_error(&self.src_reg, &err);
463 }
464 }
465
466 fn expand_str<D>(&mut self, src_id: SourceId, dst: D) -> Result<()>
471 where
472 D: AsRef<Path>,
473 {
474 if self.pointer_target_width == 0 {
475 panic!(
476 r#"pointer target width unknown,
477 set env CARGO_CFG_TARGET_POINTER_WIDTH environment variable,
478 or use `with_pointer_target_width` function
479"#
480 );
481 }
482 let items = self.init_types_map(self.pointer_target_width)?;
483
484 let syn_file = syn::parse_file(self.src_reg.src(src_id))
485 .map_err(|err| DiagnosticError::from_syn_err(src_id, err))?;
486
487 let mut file =
488 file_cache::FileWriteCache::new(dst.as_ref(), &mut file_cache::NoNeedFsOpsRegistration);
489
490 for item in items {
491 write!(&mut file, "{}", DisplayToTokens(&item)).expect(WRITE_TO_MEM_FAILED_MSG);
492 }
493
494 let mut items_to_expand = Vec::with_capacity(syn_file.items.len() / 2);
496
497 for item in syn_file.items {
498 if let syn::Item::Macro(mut item_macro) = item {
499 let is_our_macro = [
500 FOREIGNER_CLASS,
501 FOREIGN_CLASS,
502 FOREIGN_ENUM,
503 FOREIGN_INTERFACE,
504 FOREIGN_CALLBACK,
505 FOREIGN_TYPEMAP,
506 ]
507 .iter()
508 .any(|x| item_macro.mac.path.is_ident(x));
509 if !is_our_macro {
510 writeln!(&mut file, "{}", DisplayToTokens(&item_macro))
511 .expect("mem I/O failed");
512 continue;
513 }
514 debug!("Found {}", DisplayToTokens(&item_macro.mac.path));
515 if item_macro.mac.tokens.is_empty() {
516 return Err(DiagnosticError::new(
517 src_id,
518 item_macro.span(),
519 format!(
520 "missing tokens in call of macro '{}'",
521 DisplayToTokens(&item_macro.mac.path)
522 ),
523 ));
524 }
525 let mut tts = TokenStream::new();
526 mem::swap(&mut tts, &mut item_macro.mac.tokens);
527 if item_macro.mac.path.is_ident(FOREIGNER_CLASS)
528 || item_macro.mac.path.is_ident(FOREIGN_CLASS)
529 {
530 let fclass = code_parse::parse_foreigner_class(src_id, &self.config, tts)?;
531 debug!("expand_foreigner_class: self_desc {:?}", fclass.self_desc);
532 self.conv_map.register_foreigner_class(&fclass);
533 items_to_expand.push(ItemToExpand::Class(Box::new(fclass)));
534 } else if item_macro.mac.path.is_ident(FOREIGN_ENUM) {
535 let fenum = code_parse::parse_foreign_enum(src_id, tts)?;
536 items_to_expand.push(ItemToExpand::Enum(fenum));
537 } else if item_macro.mac.path.is_ident(FOREIGN_INTERFACE)
538 || item_macro.mac.path.is_ident(FOREIGN_CALLBACK)
539 {
540 let finterface = code_parse::parse_foreign_interface(src_id, tts)?;
541 items_to_expand.push(ItemToExpand::Interface(finterface));
542 } else if item_macro.mac.path.is_ident(FOREIGN_TYPEMAP) {
543 self.conv_map.parse_foreign_typemap_macro(src_id, tts)?;
544 } else {
545 unreachable!();
546 }
547 } else {
548 writeln!(&mut file, "{}", DisplayToTokens(&item)).expect("mem I/O failed");
549 }
550 }
551 let generator = Generator::language_generator(&self.config);
552 let code = generator.expand_items(
553 &mut self.conv_map,
554 self.pointer_target_width,
555 &self.foreign_lang_helpers,
556 items_to_expand,
557 self.remove_not_generated_files,
558 )?;
559 for elem in code {
560 writeln!(&mut file, "{}", elem).expect(WRITE_TO_MEM_FAILED_MSG);
561 }
562
563 let source_bytes = file.take_content();
564 let source_bytes = generator.post_proccess_code(
565 &mut self.conv_map,
566 self.pointer_target_width,
567 source_bytes,
568 )?;
569 file.replace_content(source_bytes);
570
571 if self.rustfmt_bindings {
572 let source_bytes = file.take_content();
573 let new_cnt = rustfmt_cnt(source_bytes).unwrap_or_else(|err| {
574 panic!("Error during running of rustfmt: {}", err);
575 });
576 file.replace_content(new_cnt);
577 }
578
579 file.update_file_if_necessary().unwrap_or_else(|err| {
580 panic!(
581 "Error during write to file {}: {}",
582 dst.as_ref().display(),
583 err
584 );
585 });
586 Ok(())
587 }
588
589 fn init_types_map(&mut self, target_pointer_width: usize) -> Result<Vec<syn::Item>> {
590 if self.init_done {
591 return Ok(vec![]);
592 }
593 self.init_done = true;
594 for code_id in &self.conv_map_source {
595 let code = self.src_reg.src(*code_id);
596 self.conv_map.merge(*code_id, code, target_pointer_width)?;
597 }
598
599 if self.conv_map.is_empty() {
600 return Err(DiagnosticError::new_without_src_info(
601 "After merge all \"types maps\" have no convertion code",
602 ));
603 }
604
605 Ok(self.conv_map.take_utils_code())
606 }
607
608 fn language_generator(cfg: &LanguageConfig) -> &dyn LanguageGenerator {
609 match cfg {
610 LanguageConfig::JavaConfig(ref java_cfg) => java_cfg,
611 LanguageConfig::CppConfig(ref cpp_cfg) => cpp_cfg,
612 LanguageConfig::PythonConfig(ref python_cfg) => python_cfg,
613 }
614 }
615}
616
617trait LanguageGenerator {
618 fn expand_items(
619 &self,
620 conv_map: &mut TypeMap,
621 pointer_target_width: usize,
622 code: &[SourceCode],
623 items: Vec<ItemToExpand>,
624 remove_not_generated_files: bool,
625 ) -> Result<Vec<TokenStream>>;
626
627 fn post_proccess_code(
628 &self,
629 _conv_map: &mut TypeMap,
630 _pointer_target_width: usize,
631 generated_code: Vec<u8>,
632 ) -> Result<Vec<u8>> {
633 Ok(generated_code)
634 }
635}
636
637#[doc(hidden)]
638pub fn rustfmt_cnt(source: Vec<u8>) -> io::Result<Vec<u8>> {
639 let rustfmt = which::which("rustfmt")
640 .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("{}", e)))?;
641
642 let mut cmd = Command::new(&*rustfmt);
643
644 cmd.stdin(Stdio::piped())
645 .stdout(Stdio::piped())
646 .stderr(Stdio::null());
647
648 let mut child = cmd.spawn()?;
649 let mut child_stdin = child.stdin.take().unwrap();
650 let mut child_stdout = child.stdout.take().unwrap();
651 let src_len = source.len();
652 let src = Arc::new(source);
653 let stdin_handle = ::std::thread::spawn(move || {
657 let _ = child_stdin.write_all(src.as_slice());
658 src
659 });
660
661 let mut output = Vec::with_capacity(src_len);
662 io::copy(&mut child_stdout, &mut output)?;
663 let status = child.wait()?;
664 let src = stdin_handle.join().expect(
665 "The thread writing to rustfmt's stdin doesn't do \
666 anything that could panic",
667 );
668 let src =
669 Arc::try_unwrap(src).expect("Internal error: rusftfmt_cnt should only one Arc refernce");
670 match status.code() {
671 Some(0) => Ok(output),
672 Some(2) => Err(io::Error::new(
673 io::ErrorKind::Other,
674 "Rustfmt parsing errors.".to_string(),
675 )),
676 Some(3) => {
677 println!("warning=Rustfmt could not format some lines.");
678 Ok(src)
679 }
680 _ => {
681 println!("warning=Internal rustfmt error");
682 Ok(src)
683 }
684 }
685}