diff --git a/Cargo.lock b/Cargo.lock index 8f71f5e2d..a8eb62ea9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -192,9 +192,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.58" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa1fb82fc0c281dd9671101b66b771ebbe1eaf967b96ac8740dcba4b70005ca8" +checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" dependencies = [ "unicode-ident", ] diff --git a/src/bindgen/bindings.rs b/src/bindgen/bindings.rs index cdacdadca..43d899b62 100644 --- a/src/bindgen/bindings.rs +++ b/src/bindgen/bindings.rs @@ -15,6 +15,9 @@ use crate::bindgen::config::{Config, Language}; use crate::bindgen::ir::{ Constant, Function, ItemContainer, ItemMap, Path as BindgenPath, Static, Struct, Typedef, }; +use crate::bindgen::language_backend::{ + CLikeLanguageBackend, CythonLanguageBackend, LanguageBackend, NamespaceOperation, +}; use crate::bindgen::writer::{Source, SourceWriter}; /// A bindings header that can be written. @@ -35,12 +38,6 @@ pub struct Bindings { noop: bool, } -#[derive(PartialEq, Eq)] -enum NamespaceOperation { - Open, - Close, -} - impl Bindings { #[allow(clippy::too_many_arguments)] pub(crate) fn new( @@ -204,146 +201,29 @@ impl Bindings { } } - pub fn write_headers(&self, out: &mut SourceWriter) { - if self.noop { - return; - } - - if let Some(ref f) = self.config.header { - out.new_line_if_not_start(); - write!(out, "{}", f); - out.new_line(); - } - if let Some(f) = self.config.include_guard() { - out.new_line_if_not_start(); - write!(out, "#ifndef {}", f); - out.new_line(); - write!(out, "#define {}", f); - out.new_line(); - } - if self.config.pragma_once && self.config.language != Language::Cython { - out.new_line_if_not_start(); - write!(out, "#pragma once"); - out.new_line(); - } - if self.config.include_version { - out.new_line_if_not_start(); - write!( - out, - "/* Generated with cbindgen:{} */", - crate::bindgen::config::VERSION - ); - out.new_line(); - } - if let Some(ref f) = self.config.autogen_warning { - out.new_line_if_not_start(); - write!(out, "{}", f); - out.new_line(); - } - - if self.config.no_includes - && self.config.sys_includes().is_empty() - && self.config.includes().is_empty() - && (self.config.cython.cimports.is_empty() || self.config.language != Language::Cython) - && self.config.after_includes.is_none() - { - return; - } - - out.new_line_if_not_start(); - - if !self.config.no_includes { - match self.config.language { - Language::C => { - out.write("#include "); - out.new_line(); - out.write("#include "); - out.new_line(); - if self.config.usize_is_size_t { - out.write("#include "); - out.new_line(); - } - out.write("#include "); - out.new_line(); - out.write("#include "); - out.new_line(); - } - Language::Cxx => { - out.write("#include "); - out.new_line(); - if self.config.usize_is_size_t { - out.write("#include "); - out.new_line(); - } - out.write("#include "); - out.new_line(); - out.write("#include "); - out.new_line(); - out.write("#include "); - out.new_line(); - out.write("#include "); - out.new_line(); - if self.config.enumeration.cast_assert_name.is_none() - && (self.config.enumeration.derive_mut_casts - || self.config.enumeration.derive_const_casts) - { - out.write("#include "); - out.new_line(); - } - } - Language::Cython => { - out.write( - "from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t", - ); - out.new_line(); - out.write( - "from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t", - ); - out.new_line(); - out.write("cdef extern from *"); - out.open_brace(); - out.write("ctypedef bint bool"); - out.new_line(); - out.write("ctypedef struct va_list"); - out.new_line(); - out.close_brace(false); - } + pub fn write(&self, file: F) { + match self.config.language { + Language::Cxx | Language::C => { + self.write_with_backend(file, CLikeLanguageBackend::new(self.config.clone())) } - } - - for include in self.config.sys_includes() { - write!(out, "#include <{}>", include); - out.new_line(); - } - - for include in self.config.includes() { - write!(out, "#include \"{}\"", include); - out.new_line(); - } - - if self.config.language == Language::Cython { - for (module, names) in &self.config.cython.cimports { - write!(out, "from {} cimport {}", module, names.join(", ")); - out.new_line(); + Language::Cython => { + self.write_with_backend(file, CythonLanguageBackend::new(self.config.clone())) } } - - if let Some(ref line) = self.config.after_includes { - write!(out, "{}", line); - out.new_line(); - } } - pub fn write(&self, file: F) { + pub fn write_with_backend(&self, file: F, language_backend: LB) { if self.noop { return; } - let mut out = SourceWriter::new(file, self); + let writer = Box::new(file); - self.write_headers(&mut out); + let mut out = SourceWriter::new(writer, self); - self.open_namespaces(&mut out); + language_backend.write_headers(&mut out); + + language_backend.open_close_namespaces(NamespaceOperation::Open, &mut out); for constant in &self.constants { if constant.uses_only_primitive_types() { @@ -449,17 +329,9 @@ impl Bindings { out.write("pass"); } - self.close_namespaces(&mut out); + language_backend.open_close_namespaces(NamespaceOperation::Close, &mut out); - if let Some(f) = self.config.include_guard() { - out.new_line_if_not_start(); - if self.config.language == Language::C { - write!(out, "#endif /* {} */", f); - } else { - write!(out, "#endif // {}", f); - } - out.new_line(); - } + language_backend.write_footers(&mut out); if let Some(ref f) = self.config.trailer { out.new_line_if_not_start(); write!(out, "{}", f); @@ -468,70 +340,4 @@ impl Bindings { } } } - - fn all_namespaces(&self) -> Vec<&str> { - if self.config.language != Language::Cxx && !self.config.cpp_compatible_c() { - return vec![]; - } - let mut ret = vec![]; - if let Some(ref namespace) = self.config.namespace { - ret.push(&**namespace); - } - if let Some(ref namespaces) = self.config.namespaces { - for namespace in namespaces { - ret.push(&**namespace); - } - } - ret - } - - fn open_close_namespaces(&self, op: NamespaceOperation, out: &mut SourceWriter) { - if self.config.language == Language::Cython { - if op == NamespaceOperation::Open { - out.new_line(); - let header = self.config.cython.header.as_deref().unwrap_or("*"); - write!(out, "cdef extern from {}", header); - out.open_brace(); - } else { - out.close_brace(false); - } - return; - } - - let mut namespaces = self.all_namespaces(); - if namespaces.is_empty() { - return; - } - - if op == NamespaceOperation::Close { - namespaces.reverse(); - } - - if self.config.cpp_compatible_c() { - out.new_line_if_not_start(); - out.write("#ifdef __cplusplus"); - } - - for namespace in namespaces { - out.new_line(); - match op { - NamespaceOperation::Open => write!(out, "namespace {} {{", namespace), - NamespaceOperation::Close => write!(out, "}} // namespace {}", namespace), - } - } - - out.new_line(); - if self.config.cpp_compatible_c() { - out.write("#endif // __cplusplus"); - out.new_line(); - } - } - - pub(crate) fn open_namespaces(&self, out: &mut SourceWriter) { - self.open_close_namespaces(NamespaceOperation::Open, out); - } - - pub(crate) fn close_namespaces(&self, out: &mut SourceWriter) { - self.open_close_namespaces(NamespaceOperation::Close, out); - } } diff --git a/src/bindgen/language_backend/clike.rs b/src/bindgen/language_backend/clike.rs new file mode 100644 index 000000000..6138981e8 --- /dev/null +++ b/src/bindgen/language_backend/clike.rs @@ -0,0 +1,175 @@ +use crate::bindgen::language_backend::{LanguageBackend, NamespaceOperation}; +use crate::bindgen::writer::SourceWriter; +use crate::bindgen::{Config, Language}; +use std::io::Write; + +pub struct CLikeLanguageBackend { + config: Config, +} + +impl CLikeLanguageBackend { + pub fn new(config: Config) -> Self { + Self { config } + } +} + +impl LanguageBackend for CLikeLanguageBackend { + fn write_headers(&self, out: &mut SourceWriter) { + if let Some(ref f) = self.config.header { + out.new_line_if_not_start(); + write!(out, "{}", f); + out.new_line(); + } + if let Some(f) = self.config.include_guard() { + out.new_line_if_not_start(); + write!(out, "#ifndef {}", f); + out.new_line(); + write!(out, "#define {}", f); + out.new_line(); + } + if self.config.pragma_once { + out.new_line_if_not_start(); + write!(out, "#pragma once"); + out.new_line(); + } + if self.config.include_version { + out.new_line_if_not_start(); + write!( + out, + "/* Generated with cbindgen:{} */", + crate::bindgen::config::VERSION + ); + out.new_line(); + } + if let Some(ref f) = self.config.autogen_warning { + out.new_line_if_not_start(); + write!(out, "{}", f); + out.new_line(); + } + + if self.config.no_includes + && self.config.sys_includes().is_empty() + && self.config.includes().is_empty() + && self.config.after_includes.is_none() + { + return; + } + + out.new_line_if_not_start(); + + if !self.config.no_includes { + match self.config.language { + Language::C => { + out.write("#include "); + out.new_line(); + out.write("#include "); + out.new_line(); + if self.config.usize_is_size_t { + out.write("#include "); + out.new_line(); + } + out.write("#include "); + out.new_line(); + out.write("#include "); + out.new_line(); + } + Language::Cxx => { + out.write("#include "); + out.new_line(); + if self.config.usize_is_size_t { + out.write("#include "); + out.new_line(); + } + out.write("#include "); + out.new_line(); + out.write("#include "); + out.new_line(); + out.write("#include "); + out.new_line(); + out.write("#include "); + out.new_line(); + if self.config.enumeration.cast_assert_name.is_none() + && (self.config.enumeration.derive_mut_casts + || self.config.enumeration.derive_const_casts) + { + out.write("#include "); + out.new_line(); + } + } + _ => {} + } + } + + for include in self.config.sys_includes() { + write!(out, "#include <{}>", include); + out.new_line(); + } + + for include in self.config.includes() { + write!(out, "#include \"{}\"", include); + out.new_line(); + } + + if let Some(ref line) = self.config.after_includes { + write!(out, "{}", line); + out.new_line(); + } + } + + fn open_close_namespaces(&self, op: NamespaceOperation, out: &mut SourceWriter) { + let mut namespaces = + if self.config.language != Language::Cxx && !self.config.cpp_compatible_c() { + vec![] + } else { + let mut ret = vec![]; + if let Some(ref namespace) = self.config.namespace { + ret.push(&**namespace); + } + if let Some(ref namespaces) = self.config.namespaces { + for namespace in namespaces { + ret.push(&**namespace); + } + } + ret + }; + + if namespaces.is_empty() { + return; + } + + if op == NamespaceOperation::Close { + namespaces.reverse(); + } + + if self.config.cpp_compatible_c() { + out.new_line_if_not_start(); + out.write("#ifdef __cplusplus"); + } + + for namespace in namespaces { + out.new_line(); + match op { + NamespaceOperation::Open => write!(out, "namespace {} {{", namespace), + NamespaceOperation::Close => write!(out, "}} // namespace {}", namespace), + } + } + + out.new_line(); + if self.config.cpp_compatible_c() { + out.write("#endif // __cplusplus"); + out.new_line(); + } + } + + fn write_footers(&self, out: &mut SourceWriter) { + if let Some(f) = self.config.include_guard() { + out.new_line_if_not_start(); + if self.config.language == Language::C { + write!(out, "#endif /* {} */", f); + } else { + write!(out, "#endif // {}", f); + } + out.new_line(); + } + } +} diff --git a/src/bindgen/language_backend/cython.rs b/src/bindgen/language_backend/cython.rs new file mode 100644 index 000000000..63f0bca68 --- /dev/null +++ b/src/bindgen/language_backend/cython.rs @@ -0,0 +1,87 @@ +use crate::bindgen::language_backend::{LanguageBackend, NamespaceOperation}; +use crate::bindgen::writer::SourceWriter; +use crate::bindgen::Config; +use std::io::Write; + +pub struct CythonLanguageBackend { + config: Config, +} + +impl CythonLanguageBackend { + pub fn new(config: Config) -> Self { + Self { config } + } +} + +impl LanguageBackend for CythonLanguageBackend { + fn write_headers(&self, out: &mut SourceWriter) { + if let Some(ref f) = self.config.header { + out.new_line_if_not_start(); + write!(out, "{}", f); + out.new_line(); + } + + if self.config.include_version { + out.new_line_if_not_start(); + write!( + out, + "/* Generated with cbindgen:{} */", + crate::bindgen::config::VERSION + ); + out.new_line(); + } + if let Some(ref f) = self.config.autogen_warning { + out.new_line_if_not_start(); + write!(out, "{}", f); + out.new_line(); + } + + if self.config.no_includes + && self.config.sys_includes().is_empty() + && self.config.includes().is_empty() + && (self.config.cython.cimports.is_empty()) + && self.config.after_includes.is_none() + { + return; + } + + out.new_line_if_not_start(); + + if !self.config.no_includes { + out.write("from libc.stdint cimport int8_t, int16_t, int32_t, int64_t, intptr_t"); + out.new_line(); + out.write("from libc.stdint cimport uint8_t, uint16_t, uint32_t, uint64_t, uintptr_t"); + out.new_line(); + out.write("cdef extern from *"); + out.open_brace(); + out.write("ctypedef bint bool"); + out.new_line(); + out.write("ctypedef struct va_list"); + out.new_line(); + out.close_brace(false); + } + + for (module, names) in &self.config.cython.cimports { + write!(out, "from {} cimport {}", module, names.join(", ")); + out.new_line(); + } + + if let Some(ref line) = self.config.after_includes { + write!(out, "{}", line); + out.new_line(); + } + } + + fn open_close_namespaces(&self, op: NamespaceOperation, out: &mut SourceWriter) { + if op == NamespaceOperation::Open { + out.new_line(); + let header = self.config.cython.header.as_deref().unwrap_or("*"); + write!(out, "cdef extern from {}", header); + out.open_brace(); + } else { + out.close_brace(false); + } + } + + fn write_footers(&self, _out: &mut SourceWriter) {} +} diff --git a/src/bindgen/language_backend/mod.rs b/src/bindgen/language_backend/mod.rs new file mode 100644 index 000000000..8346f19ec --- /dev/null +++ b/src/bindgen/language_backend/mod.rs @@ -0,0 +1,20 @@ +use crate::bindgen::writer::SourceWriter; +use std::io::Write; + +mod clike; +mod cython; + +pub use clike::CLikeLanguageBackend; +pub use cython::CythonLanguageBackend; + +#[derive(PartialEq, Eq)] +pub enum NamespaceOperation { + Open, + Close, +} + +pub trait LanguageBackend { + fn write_headers(&self, out: &mut SourceWriter); + fn open_close_namespaces(&self, op: NamespaceOperation, out: &mut SourceWriter); + fn write_footers(&self, out: &mut SourceWriter); +} diff --git a/src/bindgen/mod.rs b/src/bindgen/mod.rs index d0789da2e..e0619438a 100644 --- a/src/bindgen/mod.rs +++ b/src/bindgen/mod.rs @@ -54,6 +54,7 @@ mod rename; mod reserved; mod utilities; mod writer; +mod language_backend; #[allow(unused)] pub(crate) use self::cargo::*; @@ -62,4 +63,4 @@ pub use self::bindings::Bindings; pub use self::builder::Builder; pub use self::config::Profile; // disambiguate with cargo::Profile pub use self::config::*; -pub use self::error::Error; +pub use self::error::Error; \ No newline at end of file