#![doc(
html_logo_url = "https://raw.githubusercontent.com/maidsafe/QA/master/Images/maidsafe_logo.png",
html_favicon_url = "http://maidsafe.net/img/favicon.ico",
test(attr(forbid(warnings)))
)]
#![deny(unsafe_code)]
#![warn(
trivial_casts,
trivial_numeric_casts,
unused_extern_crates,
unused_import_braces,
unused_qualifications,
)]
#![allow(
missing_docs,
unused_results
)]
pub use common::FilterMode;
pub use csharp::LangCSharp;
pub use errors::Error;
pub use errors::Level;
pub use java::LangJava;
pub use lang_c::LangC;
use common::{Lang, Outputs};
use std::collections::{BTreeSet, HashMap};
use std::fs;
use std::fs::File;
use std::io::Error as IoError;
use std::io::{Read, Write};
use std::path::{self, Component, Path, PathBuf};
use unwrap::unwrap;
#[cfg(test)]
#[macro_use]
mod test_utils;
mod common;
mod csharp;
mod errors;
mod java;
mod lang_c;
mod output;
mod parse;
mod struct_field;
enum Input {
File(PathBuf),
Code { file_name: String, code: String },
}
pub struct Bindgen {
input: Input,
}
impl Bindgen {
pub fn new() -> Result<Self, Error> {
let source_path = source_file_from_cargo()?;
let input = Input::File(PathBuf::from(source_path));
Ok(Bindgen { input })
}
pub fn source_file<T>(&mut self, path: T) -> &mut Self
where
PathBuf: From<T>,
{
self.input = Input::File(PathBuf::from(path));
self
}
pub fn source_code<S>(&mut self, file_name: S, code: S) -> &mut Self
where
S: Into<String>,
{
self.input = Input::Code {
file_name: file_name.into(),
code: code.into(),
};
self
}
pub fn compile<L: Lang>(
&mut self,
lang: &mut L,
outputs: &mut Outputs,
finalise: bool,
) -> Result<(), Vec<Error>> {
match &self.input {
Input::Code { file_name, code } => {
self.compile_from_source(lang, outputs, file_name.clone(), code.clone())?;
}
Input::File(path) => {
self.compile_from_path(lang, outputs, path)?;
}
}
if finalise {
lang.finalise_output(outputs)?;
}
Ok(())
}
fn compile_from_path<L: Lang>(
&self,
lang: &mut L,
outputs: &mut Outputs,
path: &PathBuf,
) -> Result<(), Vec<Error>> {
let base_path = unwrap!(path.parent());
let mod_path: String = unwrap!(path.to_str()).to_string();
let mut file = unwrap!(File::open(path));
let mut content = String::new();
unwrap!(file.read_to_string(&mut content));
let ast = unwrap!(syn::parse_file(&content));
let mut imported: BTreeSet<Vec<String>> = Default::default();
for item in ast.items {
match &item {
syn::Item::Use(ref itemuse) => {
if parse::imported_mods(itemuse).is_some() {
imported.insert(unwrap!(parse::imported_mods(itemuse)));
}
}
syn::Item::Const(ref item) => {
lang.parse_const(item, &[mod_path.clone()], outputs)?;
}
_ => {}
}
}
for module in imported {
let mut mod_path = base_path.join(&format!(
"{}.rs",
module.join(&path::MAIN_SEPARATOR.to_string())
));
if !mod_path.exists() {
mod_path = base_path.join(&format!(
"{}/mod.rs",
module.join(&path::MAIN_SEPARATOR.to_string())
));
}
println!("Parsing {} ({:?})", module.join("::"), mod_path);
let mut file = unwrap!(File::open(mod_path));
let mut content = String::new();
unwrap!(file.read_to_string(&mut content));
let ast = unwrap!(syn::parse_file(&content));
parse::parse_file(lang, &ast, &module, outputs)?;
}
Ok(())
}
fn compile_from_source<L: Lang>(
&self,
lang: &mut L,
outputs: &mut Outputs,
file_name: String,
source: String,
) -> Result<(), Vec<Error>> {
let module = convert_lib_path_to_module(&PathBuf::from(file_name.clone()));
let _ast: syn::File = unwrap!(syn::parse_str(&source));
for item in _ast.items {
match &item {
syn::Item::Mod(ref item) => {
parse::parse_mod(lang, item, &module[..], outputs)?;
}
syn::Item::Const(ref item) => {
lang.parse_const(item, &module[..], outputs)?;
}
syn::Item::Type(ref item) => {
lang.parse_ty(item, &module[..], outputs)?;
}
syn::Item::Enum(ref item) => {
lang.parse_enum(item, &module[..], outputs)?;
}
syn::Item::Fn(ref item) => {
lang.parse_fn(item, &module[..], outputs)?;
}
syn::Item::Struct(ref item) => {
lang.parse_struct(item, &module[..], outputs)?;
}
_ => {}
}
}
Ok(())
}
pub fn compile_or_panic<L: Lang>(
&mut self,
lang: &mut L,
outputs: &mut Outputs,
finalise: bool,
) {
if let Err(errors) = self.compile(lang, outputs, finalise) {
for error in &errors {
self.print_error(error);
}
panic!("Failed to compile.");
}
}
pub fn write_outputs<P: AsRef<Path>>(&self, root: P, outputs: &Outputs) -> Result<(), IoError> {
let root = root.as_ref();
for (path, contents) in outputs {
let full_path = root.join(PathBuf::from(path));
if let Some(parent_dirs) = full_path.parent() {
fs::create_dir_all(parent_dirs)?;
}
let mut f = fs::File::create(full_path)?;
f.write_all(contents.as_bytes())?;
f.sync_all()?;
}
Ok(())
}
pub fn write_outputs_or_panic<P: AsRef<Path>>(&self, root: P, outputs: &Outputs) {
if let Err(err) = self.write_outputs(root, outputs) {
self.print_error(&From::from(err));
panic!("Failed to write output.");
}
}
pub fn run_build<P: AsRef<Path>, L: Lang>(&mut self, lang: &mut L, output_dir: P) {
let mut outputs = HashMap::new();
self.compile_or_panic(lang, &mut outputs, true);
self.write_outputs_or_panic(output_dir, &outputs);
}
pub fn print_error(&self, error: &Error) {
error.print();
}
}
fn convert_lib_path_to_module<P: AsRef<Path>>(path: &P) -> Vec<String> {
let mut res = Vec::new();
let path = path.as_ref();
for component in path.components() {
if let Component::Normal(path) = component {
let path = unwrap!(path.to_str());
res.push(path.to_string());
}
}
if res[(res.len() - 2)..] == ["src", "lib.rs"] {
res = res[..(res.len() - 2)].to_vec();
}
res
}
fn source_file_from_cargo() -> Result<String, Error> {
let cargo_toml = path::Path::new(
&std::env::var_os("CARGO_MANIFEST_DIR").unwrap_or_else(|| std::ffi::OsString::from("")),
)
.join("Cargo.toml");
let default = "src/lib.rs";
let mut cargo_toml = match std::fs::File::open(&cargo_toml) {
Ok(value) => value,
Err(..) => return Ok(default.to_owned()),
};
let mut buf = String::new();
match cargo_toml.read_to_string(&mut buf) {
Ok(..) => {}
Err(..) => {
return Err(Error {
level: Level::Fatal,
span: None,
message: "could not read cargo manifest".into(),
});
}
};
let table = match (&buf).parse::<toml::Value>() {
Ok(value) => value,
Err(..) => {
return Err(Error {
level: Level::Fatal,
span: None,
message: "could not parse cargo manifest".into(),
});
}
};
Ok(table
.get("lib")
.and_then(|t| t.get("path"))
.and_then(|s| s.as_str())
.unwrap_or(default)
.into())
}