#[macro_use]
extern crate syn;
extern crate proc_macro2;
#[macro_use]
extern crate lazy_static;
use std::collections::hash_map::DefaultHasher;
use std::env;
use std::hash::{Hash, Hasher};
use std::path::PathBuf;
use proc_macro2::{Span, TokenStream, TokenTree};
use syn::ext::IdentExt;
use syn::parse::{Parse, ParseStream, Result};
use syn::{Attribute, Ident, Type};
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
pub const LIB_NAME: &str = "librust_cpp_generated.a";
pub const MSVC_LIB_NAME: &str = "rust_cpp_generated.lib";
pub mod flags {
pub const IS_COPY_CONSTRUCTIBLE: u32 = 0;
pub const IS_DEFAULT_CONSTRUCTIBLE: u32 = 1;
pub const IS_TRIVIALLY_DESTRUCTIBLE: u32 = 2;
pub const IS_TRIVIALLY_COPYABLE: u32 = 3;
pub const IS_TRIVIALLY_DEFAULT_CONSTRUCTIBLE: u32 = 4;
}
pub mod kw {
#![allow(non_camel_case_types)]
custom_keyword!(rust);
}
#[rustfmt::skip]
pub const STRUCT_METADATA_MAGIC: [u8; 128] = [
b'r', b'u', b's', b't', b'c', b'p', b'p', b'~',
b'm', b'e', b't', b'a', b'd', b'a', b't', b'a',
92, 74, 112, 213, 165, 185, 214, 120, 179, 17, 185, 25, 182, 253, 82, 118,
148, 29, 139, 208, 59, 153, 78, 137, 230, 54, 26, 177, 232, 121, 132, 166,
44, 106, 218, 57, 158, 33, 69, 32, 54, 204, 123, 226, 99, 117, 60, 173,
112, 61, 56, 174, 117, 141, 126, 249, 79, 159, 6, 119, 2, 129, 147, 66,
135, 136, 212, 252, 231, 105, 239, 91, 96, 232, 113, 94, 164, 255, 152, 144,
64, 207, 192, 90, 225, 171, 59, 154, 60, 2, 0, 191, 114, 182, 38, 134,
134, 183, 212, 227, 31, 217, 12, 5, 65, 221, 150, 59, 230, 96, 73, 62,
];
lazy_static! {
pub static ref OUT_DIR: PathBuf = PathBuf::from(env::var("OUT_DIR").expect(
r#"
-- rust-cpp fatal error --
The OUT_DIR environment variable was not set.
NOTE: rustc must be run by Cargo."#
));
pub static ref FILE_HASH: u64 = {
let mut hasher = std::collections::hash_map::DefaultHasher::new();
OUT_DIR.hash(&mut hasher);
hasher.finish()
};
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct Capture {
pub mutable: bool,
pub name: Ident,
pub cpp: String,
}
impl Parse for Capture {
fn parse(input: ParseStream) -> Result<Self> {
Ok(Capture {
mutable: input.parse::<Option<Token![mut]>>()?.is_some(),
name: input.call(Ident::parse_any)?,
cpp: {
input.parse::<Token![as]>()?;
input.parse::<syn::LitStr>()?.value()
},
})
}
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct ClosureSig {
pub captures: Vec<Capture>,
pub ret: Option<Type>,
pub cpp: String,
pub std_body: String,
}
impl ClosureSig {
pub fn name_hash(&self) -> u64 {
let mut hasher = DefaultHasher::new();
self.hash(&mut hasher);
hasher.finish()
}
pub fn extern_name(&self) -> Ident {
Ident::new(
&format!("__cpp_closure_{}", self.name_hash()),
Span::call_site(),
)
}
}
#[derive(Clone, Debug)]
pub struct Closure {
pub sig: ClosureSig,
pub body: TokenTree,
pub body_str: String,
pub callback_offset: u32,
}
impl Parse for Closure {
fn parse(input: ParseStream) -> Result<Self> {
input.parse::<Option<Token![unsafe]>>()?;
let capture_content;
bracketed!(capture_content in input);
let captures =
syn::punctuated::Punctuated::<Capture, Token![,]>::parse_terminated(&capture_content)?
.into_iter()
.collect();
let (ret, cpp) = if input.peek(Token![->]) {
input.parse::<Token![->]>()?;
let t: syn::Type = input.parse()?;
input.parse::<Token![as]>()?;
let s = input.parse::<syn::LitStr>()?.value();
(Some(t), s)
} else {
(None, "void".to_owned())
};
let body = input.parse::<TokenTree>()?;
let std_body = body
.to_string()
.chars()
.filter(|x| !x.is_whitespace())
.collect();
Ok(Closure {
sig: ClosureSig {
captures,
ret,
cpp,
std_body,
},
body,
body_str: String::new(),
callback_offset: 0,
})
}
}
#[derive(Clone, Debug)]
pub struct Class {
pub name: Ident,
pub cpp: String,
pub attrs: Vec<Attribute>,
pub line: String,
}
impl Class {
pub fn name_hash(&self) -> u64 {
let mut hasher = DefaultHasher::new();
self.name.hash(&mut hasher);
self.cpp.hash(&mut hasher);
hasher.finish()
}
pub fn derives(&self, i: &str) -> bool {
self.attrs.iter().any(|x| {
use syn::{Meta, NestedMeta};
x.interpret_meta().map_or(false, |m| {
if let Meta::List(ref list) = m {
list.ident == "derive" && list.nested.iter().any(|y| {
if let NestedMeta::Meta(Meta::Word(ref d)) = y {
d == i
} else {
false
}
})
} else {
false
}
})
})
}
}
impl Parse for Class {
fn parse(input: ParseStream) -> Result<Self> {
Ok(Class {
attrs: input.call(Attribute::parse_outer)?,
name: {
input.parse::<syn::Visibility>()?;
input.parse::<Token![unsafe]>()?;
input.parse::<Token![struct]>()?;
input.parse()?
},
cpp: {
input.parse::<Token![as]>()?;
input.parse::<syn::LitStr>()?.value()
},
line: String::new(),
})
}
}
#[allow(clippy::large_enum_variant)]
#[derive(Debug)]
pub enum Macro {
Closure(Closure),
Lit(TokenStream),
}
impl Parse for Macro {
fn parse(input: ParseStream) -> Result<Self> {
if input.peek(syn::token::Brace) {
let content;
braced!(content in input);
return Ok(Macro::Lit(content.parse()?));
}
Ok(Macro::Closure(input.parse::<Closure>()?))
}
}
#[derive(Debug)]
pub struct RustInvocation {
pub begin: Span,
pub end: Span,
pub id: Ident,
pub return_type: Option<String>,
pub arguments: Vec<(Ident, String)>,
}
impl Parse for RustInvocation {
fn parse(input: ParseStream) -> Result<Self> {
let rust_token = input.parse::<kw::rust>()?;
input.parse::<Token![!]>()?;
let macro_content;
let p = parenthesized!(macro_content in input);
let r = RustInvocation {
begin: rust_token.span,
end: p.span,
id: macro_content.parse()?,
arguments: {
let capture_content;
bracketed!(capture_content in macro_content);
capture_content
.parse_terminated::<(Ident, String), Token![,]>(
|input: ParseStream| -> Result<(Ident, String)> {
let i = input.call(Ident::parse_any)?;
input.parse::<Token![:]>()?;
input.parse::<Type>()?;
input.parse::<Token![as]>()?;
let s = input.parse::<syn::LitStr>()?.value();
Ok((i, s))
},
)?.into_iter()
.collect()
},
return_type: if macro_content.peek(Token![->]) {
macro_content.parse::<Token![->]>()?;
macro_content.parse::<Type>()?;
macro_content.parse::<Token![as]>()?;
Some(macro_content.parse::<syn::LitStr>()?.value())
} else {
None
},
};
macro_content.parse::<TokenTree>()?;
Ok(r)
}
}