mod constants;
mod functions;
mod types;
use std::fs::File;
use std::path::Path;
use derive_builder::Builder;
use interoptopus::inventory::Inventory;
use interoptopus_backend_utils::{Error, IndentWriter};
use self::constants::write_constants;
use self::functions::write_functions;
use self::types::write_type_definitions;
#[derive(Clone, Debug, Default)]
pub struct Config {
pub lib_name: String,
pub package_name: String,
pub additional_imports: Vec<String>,
pub file_header_comment: String,
}
#[derive(Clone, Debug, Builder, Default)]
#[builder(default)]
pub struct Interop {
#[builder(setter(into))]
config: Config,
inventory: Inventory,
}
impl Interop {
#[must_use]
pub fn builder() -> InteropBuilder {
InteropBuilder::new()
}
pub fn inventory(&self) -> &Inventory {
&self.inventory
}
pub fn config(&self) -> &Config {
&self.config
}
pub fn write_to(&self, w: &mut IndentWriter) -> Result<(), Error> {
self.write_file_header(w)?;
self.write_package_declaration(w)?;
w.newline()?;
self.write_imports(w)?;
w.newline()?;
self.write_extern_class_header(w)?;
w.indent();
write_constants(self, w)?;
write_type_definitions(self, w)?;
write_functions(self, w)?;
w.unindent();
self.write_extern_class_footer(w)?;
self.write_struct_definitions(w)?;
Ok(())
}
pub fn write_file<P: AsRef<Path>>(&self, file_name: P) -> Result<(), Error> {
let mut file = File::create(file_name)?;
let mut writer = IndentWriter::new(&mut file);
self.write_to(&mut writer)
}
pub fn to_string(&self) -> Result<String, Error> {
let mut vec = Vec::new();
let mut writer = IndentWriter::new(&mut vec);
self.write_to(&mut writer)?;
Ok(String::from_utf8(vec)?)
}
fn write_file_header(&self, w: &mut IndentWriter) -> Result<(), Error> {
w.indented(|w| writeln!(w, "// <auto-generated>"))?;
w.indented(|w| writeln!(w, "//"))?;
w.indented(|w| {
writeln!(
w,
"// This file was automatically generated by Interoptopus."
)
})?;
w.indented(|w| writeln!(w, "//"))?;
w.indented(|w| writeln!(w, "// Library: {}", self.config.lib_name))?;
w.indented(|w| writeln!(w, "// Package: {}", self.config.package_name))?;
w.indented(|w| writeln!(w, "// Builder: interoptopus_backend_haxe (0.1.0)"))?;
w.indented(|w| writeln!(w, "//"))?;
w.indented(|w| writeln!(w, "// Do not edit this file manually."))?;
w.indented(|w| writeln!(w, "//"))?;
w.indented(|w| writeln!(w, "// </auto-generated>"))?;
w.newline()?;
Ok(())
}
fn write_package_declaration(&self, w: &mut IndentWriter) -> Result<(), Error> {
if !self.config.package_name.is_empty() {
w.indented(|w| writeln!(w, "package {};", self.config.package_name))?;
}
Ok(())
}
fn write_imports(&self, w: &mut IndentWriter) -> Result<(), Error> {
w.indented(|w| writeln!(w, "import cpp.Pointer;"))?;
w.indented(|w| writeln!(w, "import cpp.ConstPointer;"))?;
w.indented(|w| writeln!(w, "import cpp.RawPointer;"))?;
for import in &self.config.additional_imports {
w.indented(|w| writeln!(w, "import {};", import))?;
}
Ok(())
}
fn write_extern_class_header(&self, w: &mut IndentWriter) -> Result<(), Error> {
let class_name = if self.config.lib_name.is_empty() {
"NativeApi".to_string()
} else {
format!("{}Api", heck::AsUpperCamelCase(&self.config.lib_name))
};
w.indented(|w| writeln!(w, "/**"))?;
w.indented(|w| writeln!(w, " * Native FFI bindings for {}.", self.config.lib_name))?;
w.indented(|w| writeln!(w, " */"))?;
w.indented(|w| writeln!(w, "@:include(\"souprune_api.h\")"))?;
w.indented(|w| writeln!(w, "extern class {} {{", class_name))?;
Ok(())
}
fn write_extern_class_footer(&self, w: &mut IndentWriter) -> Result<(), Error> {
w.indented(|w| writeln!(w, "}}"))?;
Ok(())
}
fn write_struct_definitions(&self, w: &mut IndentWriter) -> Result<(), Error> {
use interoptopus::lang::Type;
for the_type in self.inventory.c_types() {
if let Type::Composite(composite) = the_type {
w.newline()?;
self.write_struct_abstract(w, composite)?;
}
}
Ok(())
}
fn write_struct_abstract(
&self,
w: &mut IndentWriter,
composite: &interoptopus::lang::Composite,
) -> Result<(), Error> {
use crate::converters::{composite_to_typename, to_haxe_type};
let struct_name = composite_to_typename(self, composite);
let native_name = struct_name.to_uppercase();
let doc_lines = composite.meta().docs().lines();
if let Some(docs) = doc_lines.first() {
w.indented(|w| writeln!(w, "/** {} */", docs))?;
}
w.indented(|w| writeln!(w, "@:structAccess"))?;
w.indented(|w| writeln!(w, "@:native(\"{}\")", native_name))?;
w.indented(|w| writeln!(w, "extern class {} {{", struct_name))?;
w.indent();
for field in composite.fields() {
let field_name = field.name();
let field_type = to_haxe_type(self, field.the_type());
let field_docs = field.docs().lines();
if let Some(doc) = field_docs.first() {
w.indented(|w| writeln!(w, "/** {} */", doc))?;
}
w.indented(|w| writeln!(w, "public var {}:{};", field_name, field_type))?;
}
w.unindent();
w.indented(|w| writeln!(w, "}}"))?;
Ok(())
}
}
impl InteropBuilder {
#[must_use]
pub fn new() -> Self {
Self::default()
}
}