mod block;
mod builtin;
mod cfg;
mod check;
pub(super) mod error;
mod file;
pub(super) mod fs;
mod ifndef;
pub(super) mod include;
mod names;
mod namespace;
mod nested;
pub(super) mod out;
mod write;
use self::cfg::UnsupportedCfgEvaluator;
use self::error::{format_err, Result};
use self::file::File;
use self::include::Include;
use crate::syntax::cfg::CfgExpr;
use crate::syntax::report::Errors;
use crate::syntax::{self, attrs, Types};
use std::collections::BTreeSet as Set;
use std::path::Path;
pub(super) use self::error::Error;
#[non_exhaustive]
pub struct Opt {
pub include: Vec<Include>,
pub cxx_impl_annotations: Option<String>,
pub cfg_evaluator: Box<dyn CfgEvaluator>,
pub(super) gen_header: bool,
pub(super) gen_implementation: bool,
pub(super) allow_dot_includes: bool,
pub(super) doxygen: bool,
}
pub trait CfgEvaluator {
fn eval(&self, name: &str, value: Option<&str>) -> CfgResult;
}
pub enum CfgResult {
True,
False,
Undetermined {
msg: String,
},
}
#[derive(Default)]
pub struct GeneratedCode {
pub header: Vec<u8>,
pub implementation: Vec<u8>,
}
impl Default for Opt {
fn default() -> Self {
Opt {
include: Vec::new(),
cxx_impl_annotations: None,
gen_header: true,
gen_implementation: true,
allow_dot_includes: true,
cfg_evaluator: Box::new(UnsupportedCfgEvaluator),
doxygen: false,
}
}
}
pub(super) fn generate_from_path(path: &Path, opt: &Opt) -> GeneratedCode {
let source = match read_to_string(path) {
Ok(source) => source,
Err(err) => format_err(path, "", err),
};
match generate_from_string(&source, opt) {
Ok(out) => out,
Err(err) => format_err(path, &source, err),
}
}
fn read_to_string(path: &Path) -> Result<String> {
let bytes = if path == Path::new("-") {
fs::read_stdin()
} else {
fs::read(path)
}?;
match String::from_utf8(bytes) {
Ok(string) => Ok(string),
Err(err) => Err(Error::Utf8(path.to_owned(), err.utf8_error())),
}
}
fn generate_from_string(source: &str, opt: &Opt) -> Result<GeneratedCode> {
let mut source = source;
if source.starts_with("#!") && !source.starts_with("#![") {
let shebang_end = source.find('\n').unwrap_or(source.len());
source = &source[shebang_end..];
}
let syntax: File = syn::parse_str(source)?;
generate(syntax, opt)
}
pub(super) fn generate(syntax: File, opt: &Opt) -> Result<GeneratedCode> {
if syntax.modules.is_empty() {
return Err(Error::NoBridgeMod);
}
let ref mut apis = Vec::new();
let ref mut errors = Errors::new();
let ref mut cfg_errors = Set::new();
for bridge in syntax.modules {
let mut cfg = CfgExpr::Unconditional;
attrs::parse(
errors,
bridge.attrs,
attrs::Parser {
cfg: Some(&mut cfg),
ignore_unrecognized: true,
..Default::default()
},
);
if cfg::eval(errors, cfg_errors, opt.cfg_evaluator.as_ref(), &cfg) {
let ref namespace = bridge.namespace;
let trusted = bridge.unsafety.is_some();
apis.extend(syntax::parse_items(
errors,
bridge.content,
trusted,
namespace,
));
}
}
cfg::strip(errors, cfg_errors, opt.cfg_evaluator.as_ref(), apis);
errors.propagate()?;
let ref types = Types::collect(errors, apis);
check::precheck(errors, apis, opt);
errors.propagate()?;
let generator = check::Generator::Build;
check::typecheck(errors, apis, types, generator);
errors.propagate()?;
let (mut header, mut implementation) = Default::default();
if opt.gen_header {
header = write::gen(apis, types, opt, true);
}
if opt.gen_implementation {
implementation = write::gen(apis, types, opt, false);
}
Ok(GeneratedCode {
header,
implementation,
})
}