blob: 4c3a292f990136fea736b09af5d15c426158d99d [file] [log] [blame]
// Functionality that is shared between the cxx::generate_bridge entry point and
// the cmd.
mod error;
pub(super) mod include;
pub(super) mod out;
mod write;
use self::error::format_err;
use crate::syntax::namespace::Namespace;
use crate::syntax::{self, check, Types};
use quote::quote;
use std::fs;
use std::io;
use std::path::Path;
use syn::{Attribute, File, Item};
use thiserror::Error;
pub(super) type Result<T, E = Error> = std::result::Result<T, E>;
#[derive(Error, Debug)]
pub(super) enum Error {
#[error("no #[cxx::bridge] module found")]
NoBridgeMod,
#[error("#[cxx::bridge] module must have inline contents")]
OutOfLineMod,
#[error(transparent)]
Io(#[from] io::Error),
#[error(transparent)]
Syn(#[from] syn::Error),
}
struct Input {
namespace: Namespace,
module: Vec<Item>,
}
#[derive(Default)]
pub(super) struct Opt {
/// Any additional headers to #include
pub include: Vec<String>,
}
pub(super) fn do_generate_bridge(path: &Path, opt: Opt) -> Vec<u8> {
let header = false;
generate(path, opt, header)
}
pub(super) fn do_generate_header(path: &Path, opt: Opt) -> Vec<u8> {
let header = true;
generate(path, opt, header)
}
fn generate(path: &Path, opt: Opt, header: bool) -> Vec<u8> {
let source = match fs::read_to_string(path) {
Ok(source) => source,
Err(err) => format_err(path, "", Error::Io(err)),
};
match (|| -> Result<_> {
let syntax = syn::parse_file(&source)?;
let bridge = find_bridge_mod(syntax)?;
let apis = syntax::parse_items(bridge.module)?;
let types = Types::collect(&apis)?;
check::typecheck(&apis, &types)?;
let out = write::gen(bridge.namespace, &apis, &types, opt, header);
Ok(out)
})() {
Ok(out) => out.content(),
Err(err) => format_err(path, &source, err),
}
}
fn find_bridge_mod(syntax: File) -> Result<Input> {
for item in syntax.items {
if let Item::Mod(item) = item {
for attr in &item.attrs {
let path = &attr.path;
if quote!(#path).to_string() == "cxx :: bridge" {
let module = match item.content {
Some(module) => module.1,
None => {
return Err(Error::Syn(syn::Error::new_spanned(
item,
Error::OutOfLineMod,
)));
}
};
let namespace = parse_args(attr)?;
return Ok(Input { namespace, module });
}
}
}
}
Err(Error::NoBridgeMod)
}
fn parse_args(attr: &Attribute) -> syn::Result<Namespace> {
if attr.tokens.is_empty() {
Ok(Namespace::none())
} else {
attr.parse_args()
}
}