Adding library for high-level code generators.
The intention here is to provide a way for high level code generators
to convert a Rust TokenStream into C++ bindings code without the need
to write files to disk or invoke an external command.
diff --git a/gen/src/mod.rs b/gen/src/mod.rs
index 710bca2..acc7669 100644
--- a/gen/src/mod.rs
+++ b/gen/src/mod.rs
@@ -10,20 +10,23 @@
#[cfg(test)]
mod tests;
-use self::error::{format_err, Error, Result};
+use self::error::format_err;
+pub use self::error::{Error, Result};
use crate::syntax::namespace::Namespace;
use crate::syntax::report::Errors;
use crate::syntax::{self, check, Types};
+use proc_macro2::TokenStream;
+use std::clone::Clone;
use std::fs;
use std::path::Path;
-use syn::Item;
+use syn::{File, Item};
struct Input {
namespace: Namespace,
module: Vec<Item>,
}
-#[derive(Default)]
+#[derive(Default, Clone)]
pub(super) struct Opt {
/// Any additional headers to #include
pub include: Vec<String>,
@@ -32,6 +35,14 @@
pub cxx_impl_annotations: Option<String>,
}
+/// Results of code generation.
+pub struct GeneratedCode {
+ /// The bytes of a C++ header file.
+ pub header: Vec<u8>,
+ /// The bytes of a C++ implementation file (e.g. .cc, cpp etc.)
+ pub cxx: Vec<u8>,
+}
+
pub(super) fn do_generate_bridge(path: &Path, opt: Opt) -> Vec<u8> {
let header = false;
generate_from_path(path, opt, header)
@@ -42,21 +53,43 @@
generate_from_path(path, opt, header)
}
+pub(super) fn do_generate_from_tokens(
+ tokens: TokenStream,
+ opt: Opt,
+) -> std::result::Result<GeneratedCode, Error> {
+ let syntax = syn::parse2::<File>(tokens)?;
+ match generate(syntax, opt, true, true) {
+ Ok((Some(header), Some(cxx))) => Ok(GeneratedCode { header, cxx } ),
+ Err(err) => Err(err),
+ _ => panic!("Unexpected generation"),
+ }
+}
+
fn generate_from_path(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 generate(&source, opt, header) {
+ let syntax = match syn::parse_file(&source) {
Ok(out) => out,
+ Err(err) => format_err(path, "", Error::Syn(err)),
+ };
+ match generate(syntax, opt, header, !header) {
+ Ok((Some(hdr), None)) => hdr,
+ Ok((None, Some(cxx))) => cxx,
Err(err) => format_err(path, &source, err),
+ _ => panic!("Unexpected generation"),
}
}
-fn generate(source: &str, opt: Opt, header: bool) -> Result<Vec<u8>> {
+fn generate(
+ syntax: File,
+ opt: Opt,
+ gen_header: bool,
+ gen_cxx: bool,
+) -> Result<(Option<Vec<u8>>, Option<Vec<u8>>)> {
proc_macro2::fallback::force();
let ref mut errors = Errors::new();
- let syntax = syn::parse_file(&source)?;
let bridge = find::find_bridge_mod(syntax)?;
let ref namespace = bridge.namespace;
let ref apis = syntax::parse_items(errors, bridge.module);
@@ -64,6 +97,18 @@
errors.propagate()?;
check::typecheck(errors, namespace, apis, types);
errors.propagate()?;
- let out = write::gen(namespace, apis, types, opt, header);
- Ok(out.content())
+ // Some callers may wish to generate both header and C++
+ // from the same token stream to avoid parsing twice. But others
+ // only need to generate one or the other.
+ let hdr = if gen_header {
+ Some(write::gen(namespace, apis, types, opt.clone(), true).content())
+ } else {
+ None
+ };
+ let cxx = if gen_cxx {
+ Some(write::gen(namespace, apis, types, opt, false).content())
+ } else {
+ None
+ };
+ Ok((hdr, cxx))
}