summaryrefslogtreecommitdiffstats
path: root/ffi-macros/src/rust2c.rs
diff options
context:
space:
mode:
Diffstat (limited to 'ffi-macros/src/rust2c.rs')
-rw-r--r--ffi-macros/src/rust2c.rs165
1 files changed, 165 insertions, 0 deletions
diff --git a/ffi-macros/src/rust2c.rs b/ffi-macros/src/rust2c.rs
new file mode 100644
index 00000000..84117ce5
--- /dev/null
+++ b/ffi-macros/src/rust2c.rs
@@ -0,0 +1,165 @@
+extern crate syn;
+use syn::export::ToTokens;
+extern crate proc_macro2;
+
+const PREFIX: &'static str = "pgp_";
+
+/// Derives the C type from the Rust type.
+///
+/// Returns the corresponding C type, and whether or not that type is
+/// a pointer type.
+fn ident2c(ident: &syn::Ident) -> (String, bool) {
+ let ident_string = ident.to_string();
+
+ // Special cases :(
+ match ident_string.as_str() {
+ "KeyID" => return ("pgp_keyid_t".into(), true),
+ "TPKBuilder" => return ("pgp_tpk_builder_t".into(), true),
+ "UserID" => return ("pgp_userid_t".into(), true),
+ "UserIDBinding" => return ("pgp_user_id_binding_t".into(), true),
+ "UserIDBindingIter" =>
+ return ("pgp_user_id_binding_iter_t".into(), true),
+
+ // Types from the libc crate.
+ "c_char" => return ("char".into(), false),
+ "c_int" => return ("int".into(), false),
+ "c_uint" => return ("uint".into(), false),
+ "bool" => return ("bool".into(), false),
+ "size_t" | "ssize_t" | "time_t" |
+ "int8_t" | "int16_t" | "int32_t" | "int64_t" |
+ "uint8_t" | "uint16_t" | "uint32_t" | "uint64_t"
+ => return (ident_string.clone(), false),
+ _ => (),
+ }
+
+ let mut s = String::new();
+ s += PREFIX;
+ let mut last_was_uppercase = false;
+ for (i, c) in ident_string.chars().enumerate() {
+ if c.is_uppercase() {
+ if ! last_was_uppercase && i > 0 {
+ s += "_";
+ }
+ s += &c.to_lowercase().to_string();
+ } else {
+ s += &c.to_string();
+ }
+
+ last_was_uppercase = c.is_uppercase();
+ }
+
+ s += "_t";
+ (s, true)
+}
+
+fn type2c<T: ToTokens>(typ: T) -> String {
+ let mut tokens = proc_macro2::TokenStream::new();
+ typ.to_tokens(&mut tokens);
+ let mut c_typ = String::new();
+ let mut is_mutable = false;
+ let mut is_pointer = 0;
+ let mut ident = None;
+ for tok in tokens {
+ use proc_macro2::TokenTree::*;
+ if false {
+ eprintln!("mut: {}, ptr: {}, ident: {:?}, tok: {:?}",
+ is_mutable, is_pointer, ident, tok);
+ }
+
+ match tok {
+ Ident(ref i) => {
+ let i_ = format!("{}", i);
+ match i_.as_str() {
+ "mut" => is_mutable = true,
+ "const" => is_mutable = false,
+ "Option" => (),
+ "Maybe" => {
+ is_pointer += 1;
+ is_mutable = true;
+ }
+ _ => {
+ if is_pointer > 0 && ! is_mutable {
+ c_typ += "const ";
+ }
+
+ ident = Some(i.clone());
+ },
+ }
+ },
+ Punct(ref p) => {
+ if ident.is_some() && p.as_char() != ':' {
+ // We already found the ident, now skip the <...>.
+ break;
+ }
+ match p.as_char() {
+ '*' | '&' => {
+ is_pointer += 1;
+ },
+ _ => (),
+ }
+ //eprintln!("{:?}", p);
+ },
+ Literal(ref l) => panic!("Unexpected {:?}", l),
+ Group(ref g) => panic!("Unexpected {:?}", g),
+ }
+ }
+ if let Some(c_ident) = ident {
+ let (c_ident, is_pointer_type) = ident2c(&c_ident);
+ if is_pointer_type {
+ is_pointer -= 1;
+ }
+ c_typ += &c_ident;
+ } else {
+ panic!();
+ }
+
+ c_typ.push(' ');
+ while is_pointer > 0 {
+ c_typ.push('*');
+ is_pointer -= 1;
+ }
+
+ //eprintln!("==> {:?} // {}", c_typ, is_pointer);
+ c_typ
+}
+
+pub fn rust2c(fun: &syn::ItemFn) -> String {
+ let decl = &fun.decl;
+ let return_type = match &decl.output {
+ syn::ReturnType::Default => "void".into(),
+ syn::ReturnType::Type(_, ref typ) => type2c(typ).trim_end().to_string(),
+ };
+ let fun_ident = format!("{}", fun.ident);
+
+ let mut s = String::new();
+ s += &format!("{}\n{} (", return_type, fun_ident);
+ let indent = fun_ident.len() + 2;
+
+ for (i, arg) in decl.inputs.iter().enumerate() {
+ // All but the first line need to be indented.
+ if i > 0 {
+ for _ in 0..indent {
+ s.push(' ');
+ }
+ }
+
+ match arg {
+ &syn::FnArg::Captured(ref cap) => {
+ let pat_ident = match &cap.pat {
+ &syn::Pat::Ident(ref i) => i,
+ _ => unimplemented!(),
+ };
+ s += &format!("{}{}", type2c(&cap.ty), pat_ident.ident);
+ },
+ _ => (),
+ }
+
+ // All but the last one need a comma.
+ if i < decl.inputs.len() - 1 {
+ s += ",\n";
+ }
+ }
+
+ s += ");";
+ s
+}