summaryrefslogtreecommitdiffstats
path: root/third_party/rust/diplomat/src
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/diplomat/src')
-rw-r--r--third_party/rust/diplomat/src/enum_convert.rs85
-rw-r--r--third_party/rust/diplomat/src/lib.rs691
-rw-r--r--third_party/rust/diplomat/src/snapshots/diplomat__tests__cfgd_struct.snap23
-rw-r--r--third_party/rust/diplomat/src/snapshots/diplomat__tests__cfged_method-2.snap24
-rw-r--r--third_party/rust/diplomat/src/snapshots/diplomat__tests__cfged_method.snap22
-rw-r--r--third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_mutable_slice.snap20
-rw-r--r--third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_mutable_str.snap26
-rw-r--r--third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_slice.snap20
-rw-r--r--third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_str.snap23
-rw-r--r--third_party/rust/diplomat/src/snapshots/diplomat__tests__mod_with_enum.snap23
-rw-r--r--third_party/rust/diplomat/src/snapshots/diplomat__tests__mod_with_rust_result.snap20
-rw-r--r--third_party/rust/diplomat/src/snapshots/diplomat__tests__mod_with_writeable_result.snap25
-rw-r--r--third_party/rust/diplomat/src/snapshots/diplomat__tests__multilevel_borrows.snap45
-rw-r--r--third_party/rust/diplomat/src/snapshots/diplomat__tests__self_params.snap22
-rw-r--r--third_party/rust/diplomat/src/transparent_convert.rs30
15 files changed, 1099 insertions, 0 deletions
diff --git a/third_party/rust/diplomat/src/enum_convert.rs b/third_party/rust/diplomat/src/enum_convert.rs
new file mode 100644
index 0000000000..0244f320e6
--- /dev/null
+++ b/third_party/rust/diplomat/src/enum_convert.rs
@@ -0,0 +1,85 @@
+use quote::{quote, ToTokens};
+use syn::parse::{Parse, ParseStream};
+use syn::*;
+
+// An attribute that is a list of idents
+pub struct EnumConvertAttribute {
+ path: Path,
+
+ needs_wildcard: bool,
+}
+
+impl Parse for EnumConvertAttribute {
+ fn parse(input: ParseStream) -> Result<Self> {
+ let paths = input.parse_terminated(Path::parse, Token![,])?;
+ if paths.is_empty() {
+ return Err(input.error("#[diplomat::enum_convert] needs a path argument"));
+ }
+ let needs_wildcard = if paths.len() == 2 {
+ if let Some(ident) = paths[1].get_ident() {
+ if ident == "needs_wildcard" {
+ true
+ } else {
+ return Err(input.error(
+ "#[diplomat::enum_convert] only recognizes needs_wildcard keyword",
+ ));
+ }
+ } else {
+ return Err(
+ input.error("#[diplomat::enum_convert] only recognizes needs_wildcard keyword")
+ );
+ }
+ } else if paths.len() > 1 {
+ return Err(input.error("#[diplomat::enum_convert] only supports up to two arguments"));
+ } else {
+ // no needs_wildcard marker
+ false
+ };
+ Ok(EnumConvertAttribute {
+ path: paths[0].clone(),
+ needs_wildcard,
+ })
+ }
+}
+
+pub fn gen_enum_convert(attr: EnumConvertAttribute, input: ItemEnum) -> proc_macro2::TokenStream {
+ let mut from_arms = vec![];
+ let mut into_arms = vec![];
+
+ let this_name = &input.ident;
+ let other_name = &attr.path;
+ for variant in &input.variants {
+ if variant.fields != Fields::Unit {
+ return Error::new(variant.ident.span(), "variant may not have fields")
+ .to_compile_error();
+ }
+
+ let variant_name = &variant.ident;
+ from_arms.push(quote!(#other_name::#variant_name => Self::#variant_name));
+ into_arms.push(quote!(#this_name::#variant_name => Self::#variant_name));
+ }
+
+ if attr.needs_wildcard {
+ let error = format!(
+ "Encountered unknown field for {}",
+ other_name.to_token_stream()
+ );
+ from_arms.push(quote!(_ => unreachable!(#error)))
+ }
+ quote! {
+ impl From<#other_name> for #this_name {
+ fn from(other: #other_name) -> Self {
+ match other {
+ #(#from_arms,)*
+ }
+ }
+ }
+ impl From<#this_name> for #other_name {
+ fn from(this: #this_name) -> Self {
+ match this {
+ #(#into_arms,)*
+ }
+ }
+ }
+ }
+}
diff --git a/third_party/rust/diplomat/src/lib.rs b/third_party/rust/diplomat/src/lib.rs
new file mode 100644
index 0000000000..dad44bff62
--- /dev/null
+++ b/third_party/rust/diplomat/src/lib.rs
@@ -0,0 +1,691 @@
+use proc_macro2::Span;
+use quote::{quote, ToTokens};
+use syn::*;
+
+use diplomat_core::ast;
+
+mod enum_convert;
+mod transparent_convert;
+
+fn cfgs_to_stream(attrs: &[Attribute]) -> proc_macro2::TokenStream {
+ attrs
+ .iter()
+ .fold(quote!(), |prev, attr| quote!(#prev #attr))
+}
+
+fn gen_params_at_boundary(param: &ast::Param, expanded_params: &mut Vec<FnArg>) {
+ match &param.ty {
+ ast::TypeName::StrReference(_) | ast::TypeName::PrimitiveSlice(..) => {
+ let data_type = if let ast::TypeName::PrimitiveSlice(.., prim) = &param.ty {
+ ast::TypeName::Primitive(*prim).to_syn().to_token_stream()
+ } else {
+ quote! { u8 }
+ };
+ expanded_params.push(FnArg::Typed(PatType {
+ attrs: vec![],
+ pat: Box::new(Pat::Ident(PatIdent {
+ attrs: vec![],
+ by_ref: None,
+ mutability: None,
+ ident: Ident::new(&format!("{}_diplomat_data", param.name), Span::call_site()),
+ subpat: None,
+ })),
+ colon_token: syn::token::Colon(Span::call_site()),
+ ty: Box::new(
+ parse2({
+ if let ast::TypeName::PrimitiveSlice(_, ast::Mutability::Mutable, _) =
+ &param.ty
+ {
+ quote! { *mut #data_type }
+ } else {
+ quote! { *const #data_type }
+ }
+ })
+ .unwrap(),
+ ),
+ }));
+
+ expanded_params.push(FnArg::Typed(PatType {
+ attrs: vec![],
+ pat: Box::new(Pat::Ident(PatIdent {
+ attrs: vec![],
+ by_ref: None,
+ mutability: None,
+ ident: Ident::new(&format!("{}_diplomat_len", param.name), Span::call_site()),
+ subpat: None,
+ })),
+ colon_token: syn::token::Colon(Span::call_site()),
+ ty: Box::new(
+ parse2(quote! {
+ usize
+ })
+ .unwrap(),
+ ),
+ }));
+ }
+ o => {
+ expanded_params.push(FnArg::Typed(PatType {
+ attrs: vec![],
+ pat: Box::new(Pat::Ident(PatIdent {
+ attrs: vec![],
+ by_ref: None,
+ mutability: None,
+ ident: Ident::new(param.name.as_str(), Span::call_site()),
+ subpat: None,
+ })),
+ colon_token: syn::token::Colon(Span::call_site()),
+ ty: Box::new(o.to_syn()),
+ }));
+ }
+ }
+}
+
+fn gen_params_invocation(param: &ast::Param, expanded_params: &mut Vec<Expr>) {
+ match &param.ty {
+ ast::TypeName::StrReference(_) | ast::TypeName::PrimitiveSlice(..) => {
+ let data_ident =
+ Ident::new(&format!("{}_diplomat_data", param.name), Span::call_site());
+ let len_ident = Ident::new(&format!("{}_diplomat_len", param.name), Span::call_site());
+
+ let tokens = if let ast::TypeName::PrimitiveSlice(_, mutability, _) = &param.ty {
+ match mutability {
+ ast::Mutability::Mutable => quote! {
+ unsafe { core::slice::from_raw_parts_mut(#data_ident, #len_ident) }
+ },
+ ast::Mutability::Immutable => quote! {
+ unsafe { core::slice::from_raw_parts(#data_ident, #len_ident) }
+ },
+ }
+ } else {
+ // TODO(#57): don't just unwrap? or should we assume that the other side gives us a good value?
+ quote! {
+ unsafe {
+ core::str::from_utf8(core::slice::from_raw_parts(#data_ident, #len_ident)).unwrap()
+ }
+ }
+ };
+ expanded_params.push(parse2(tokens).unwrap());
+ }
+ ast::TypeName::Result(_, _, _) => {
+ let param = &param.name;
+ expanded_params.push(parse2(quote!(#param.into())).unwrap());
+ }
+ _ => {
+ expanded_params.push(Expr::Path(ExprPath {
+ attrs: vec![],
+ qself: None,
+ path: Ident::new(param.name.as_str(), Span::call_site()).into(),
+ }));
+ }
+ }
+}
+
+fn gen_custom_type_method(strct: &ast::CustomType, m: &ast::Method) -> Item {
+ let self_ident = Ident::new(strct.name().as_str(), Span::call_site());
+ let method_ident = Ident::new(m.name.as_str(), Span::call_site());
+ let extern_ident = Ident::new(m.full_path_name.as_str(), Span::call_site());
+
+ let mut all_params = vec![];
+ m.params.iter().for_each(|p| {
+ gen_params_at_boundary(p, &mut all_params);
+ });
+
+ let mut all_params_invocation = vec![];
+ m.params.iter().for_each(|p| {
+ gen_params_invocation(p, &mut all_params_invocation);
+ });
+
+ let this_ident = Pat::Ident(PatIdent {
+ attrs: vec![],
+ by_ref: None,
+ mutability: None,
+ ident: Ident::new("this", Span::call_site()),
+ subpat: None,
+ });
+
+ if let Some(self_param) = &m.self_param {
+ all_params.insert(
+ 0,
+ FnArg::Typed(PatType {
+ attrs: vec![],
+ pat: Box::new(this_ident.clone()),
+ colon_token: syn::token::Colon(Span::call_site()),
+ ty: Box::new(self_param.to_typename().to_syn()),
+ }),
+ );
+ }
+
+ let lifetimes = {
+ let lifetime_env = &m.lifetime_env;
+ if lifetime_env.is_empty() {
+ quote! {}
+ } else {
+ quote! { <#lifetime_env> }
+ }
+ };
+
+ let method_invocation = if m.self_param.is_some() {
+ quote! { #this_ident.#method_ident }
+ } else {
+ quote! { #self_ident::#method_ident }
+ };
+
+ let (return_tokens, maybe_into) = if let Some(return_type) = &m.return_type {
+ if let ast::TypeName::Result(ok, err, true) = return_type {
+ let ok = ok.to_syn();
+ let err = err.to_syn();
+ (
+ quote! { -> diplomat_runtime::DiplomatResult<#ok, #err> },
+ quote! { .into() },
+ )
+ } else {
+ let return_type_syn = return_type.to_syn();
+ (quote! { -> #return_type_syn }, quote! {})
+ }
+ } else {
+ (quote! {}, quote! {})
+ };
+
+ let writeable_flushes = m
+ .params
+ .iter()
+ .filter(|p| p.is_writeable())
+ .map(|p| {
+ let p = &p.name;
+ quote! { #p.flush(); }
+ })
+ .collect::<Vec<_>>();
+
+ let cfg = cfgs_to_stream(&m.attrs.cfg);
+
+ if writeable_flushes.is_empty() {
+ Item::Fn(syn::parse_quote! {
+ #[no_mangle]
+ #cfg
+ extern "C" fn #extern_ident#lifetimes(#(#all_params),*) #return_tokens {
+ #method_invocation(#(#all_params_invocation),*) #maybe_into
+ }
+ })
+ } else {
+ Item::Fn(syn::parse_quote! {
+ #[no_mangle]
+ #cfg
+ extern "C" fn #extern_ident#lifetimes(#(#all_params),*) #return_tokens {
+ let ret = #method_invocation(#(#all_params_invocation),*);
+ #(#writeable_flushes)*
+ ret #maybe_into
+ }
+ })
+ }
+}
+
+struct AttributeInfo {
+ repr: bool,
+ opaque: bool,
+}
+
+impl AttributeInfo {
+ fn extract(attrs: &mut Vec<Attribute>) -> Self {
+ let mut repr = false;
+ let mut opaque = false;
+ attrs.retain(|attr| {
+ let ident = &attr.path().segments.iter().next().unwrap().ident;
+ if ident == "repr" {
+ repr = true;
+ // don't actually extract repr attrs, just detect them
+ return true;
+ } else if ident == "diplomat" {
+ if attr.path().segments.len() == 2 {
+ let seg = &attr.path().segments.iter().nth(1).unwrap().ident;
+ if seg == "opaque" {
+ opaque = true;
+ return false;
+ } else if seg == "rust_link" || seg == "out" || seg == "attr" {
+ // diplomat-tool reads these, not diplomat::bridge.
+ // throw them away so rustc doesn't complain about unknown attributes
+ return false;
+ } else if seg == "enum_convert" || seg == "transparent_convert" {
+ // diplomat::bridge doesn't read this, but it's handled separately
+ // as an attribute
+ return true;
+ } else {
+ panic!("Only #[diplomat::opaque] and #[diplomat::rust_link] are supported")
+ }
+ } else {
+ panic!("#[diplomat::foo] attrs have a single-segment path name")
+ }
+ }
+ true
+ });
+
+ Self { repr, opaque }
+ }
+}
+
+fn gen_bridge(input: ItemMod) -> ItemMod {
+ let module = ast::Module::from_syn(&input, true);
+ let (brace, mut new_contents) = input.content.unwrap();
+
+ new_contents.iter_mut().for_each(|c| match c {
+ Item::Struct(s) => {
+ let info = AttributeInfo::extract(&mut s.attrs);
+ if info.opaque || !info.repr {
+ let repr = if info.opaque {
+ // Normal opaque types don't need repr(transparent) because the inner type is
+ // never referenced. #[diplomat::transparent_convert] handles adding repr(transparent)
+ // on its own
+ quote!()
+ } else {
+ quote!(#[repr(C)])
+ };
+ *s = syn::parse_quote! {
+ #repr
+ #s
+ }
+ }
+ }
+
+ Item::Enum(e) => {
+ let info = AttributeInfo::extract(&mut e.attrs);
+ if info.opaque {
+ panic!("#[diplomat::opaque] not allowed on enums")
+ }
+ for v in &mut e.variants {
+ let info = AttributeInfo::extract(&mut v.attrs);
+ if info.opaque {
+ panic!("#[diplomat::opaque] not allowed on enum variants");
+ }
+ }
+ *e = syn::parse_quote! {
+ #[repr(C)]
+ #e
+ };
+ }
+
+ Item::Impl(i) => {
+ for item in &mut i.items {
+ if let syn::ImplItem::Fn(ref mut m) = *item {
+ let info = AttributeInfo::extract(&mut m.attrs);
+ if info.opaque {
+ panic!("#[diplomat::opaque] not allowed on methods")
+ }
+ }
+ }
+ }
+ _ => (),
+ });
+
+ for custom_type in module.declared_types.values() {
+ custom_type.methods().iter().for_each(|m| {
+ new_contents.push(gen_custom_type_method(custom_type, m));
+ });
+
+ let destroy_ident = Ident::new(
+ format!("{}_destroy", custom_type.name()).as_str(),
+ Span::call_site(),
+ );
+
+ let type_ident = custom_type.name().to_syn();
+
+ let (lifetime_defs, lifetimes) = if let Some(lifetime_env) = custom_type.lifetimes() {
+ (
+ quote! { <#lifetime_env> },
+ lifetime_env.lifetimes_to_tokens(),
+ )
+ } else {
+ (quote! {}, quote! {})
+ };
+
+ let cfg = cfgs_to_stream(&custom_type.attrs().cfg);
+
+ // for now, body is empty since all we need to do is drop the box
+ // TODO(#13): change to take a `*mut` and handle DST boxes appropriately
+ new_contents.push(Item::Fn(syn::parse_quote! {
+ #[no_mangle]
+ #cfg
+ extern "C" fn #destroy_ident#lifetime_defs(this: Box<#type_ident#lifetimes>) {}
+ }));
+ }
+
+ ItemMod {
+ attrs: input.attrs,
+ vis: input.vis,
+ mod_token: input.mod_token,
+ ident: input.ident,
+ content: Some((brace, new_contents)),
+ semi: input.semi,
+ unsafety: None,
+ }
+}
+
+/// Mark a module to be exposed through Diplomat-generated FFI.
+#[proc_macro_attribute]
+pub fn bridge(
+ _attr: proc_macro::TokenStream,
+ input: proc_macro::TokenStream,
+) -> proc_macro::TokenStream {
+ let expanded = gen_bridge(parse_macro_input!(input));
+ proc_macro::TokenStream::from(expanded.to_token_stream())
+}
+
+/// Generate From and Into implementations for a Diplomat enum
+///
+/// This is invoked as `#[diplomat::enum_convert(OtherEnumName)]`
+/// on a Diplomat enum. It will assume the other enum has exactly the same variants
+/// and generate From and Into implementations using those. In case that enum is `#[non_exhaustive]`,
+/// you may use `#[diplomat::enum_convert(OtherEnumName, needs_wildcard)]` to generate a panicky wildcard
+/// branch. It is up to the library author to ensure the enums are kept in sync. You may use the `#[non_exhaustive_omitted_patterns]`
+/// lint to enforce this.
+#[proc_macro_attribute]
+pub fn enum_convert(
+ attr: proc_macro::TokenStream,
+ input: proc_macro::TokenStream,
+) -> proc_macro::TokenStream {
+ // proc macros handle compile errors by using special error tokens.
+ // In case of an error, we don't want the original code to go away too
+ // (otherwise that will cause more errors) so we hold on to it and we tack it in
+ // with no modifications below
+ let input_cached: proc_macro2::TokenStream = input.clone().into();
+ let expanded =
+ enum_convert::gen_enum_convert(parse_macro_input!(attr), parse_macro_input!(input));
+
+ let full = quote! {
+ #expanded
+ #input_cached
+ };
+ proc_macro::TokenStream::from(full.to_token_stream())
+}
+
+/// Generate conversions from inner types for opaque Diplomat types with a single field
+///
+/// This is invoked as `#[diplomat::transparent_convert]`
+/// on an opaque Diplomat type. It will add `#[repr(transparent)]` and implement `pub(crate) fn transparent_convert()`
+/// which allows constructing an `&Self` from a reference to the inner field.
+#[proc_macro_attribute]
+pub fn transparent_convert(
+ _attr: proc_macro::TokenStream,
+ input: proc_macro::TokenStream,
+) -> proc_macro::TokenStream {
+ // proc macros handle compile errors by using special error tokens.
+ // In case of an error, we don't want the original code to go away too
+ // (otherwise that will cause more errors) so we hold on to it and we tack it in
+ // with no modifications below
+ let input_cached: proc_macro2::TokenStream = input.clone().into();
+ let expanded = transparent_convert::gen_transparent_convert(parse_macro_input!(input));
+
+ let full = quote! {
+ #expanded
+ #input_cached
+ };
+ proc_macro::TokenStream::from(full.to_token_stream())
+}
+
+#[cfg(test)]
+mod tests {
+ use std::fs::File;
+ use std::io::{Read, Write};
+ use std::process::Command;
+
+ use quote::ToTokens;
+ use syn::parse_quote;
+ use tempfile::tempdir;
+
+ use super::gen_bridge;
+
+ fn rustfmt_code(code: &str) -> String {
+ let dir = tempdir().unwrap();
+ let file_path = dir.path().join("temp.rs");
+ let mut file = File::create(file_path.clone()).unwrap();
+
+ writeln!(file, "{code}").unwrap();
+ drop(file);
+
+ Command::new("rustfmt")
+ .arg(file_path.to_str().unwrap())
+ .spawn()
+ .unwrap()
+ .wait()
+ .unwrap();
+
+ let mut file = File::open(file_path).unwrap();
+ let mut data = String::new();
+ file.read_to_string(&mut data).unwrap();
+ drop(file);
+ dir.close().unwrap();
+ data
+ }
+
+ #[test]
+ fn method_taking_str() {
+ insta::assert_display_snapshot!(rustfmt_code(
+ &gen_bridge(parse_quote! {
+ mod ffi {
+ struct Foo {}
+
+ impl Foo {
+ pub fn from_str(s: &str) {
+ unimplemented!()
+ }
+ }
+ }
+ })
+ .to_token_stream()
+ .to_string()
+ ));
+ }
+
+ #[test]
+ fn method_taking_slice() {
+ insta::assert_display_snapshot!(rustfmt_code(
+ &gen_bridge(parse_quote! {
+ mod ffi {
+ struct Foo {}
+
+ impl Foo {
+ pub fn from_slice(s: &[f64]) {
+ unimplemented!()
+ }
+ }
+ }
+ })
+ .to_token_stream()
+ .to_string()
+ ));
+ }
+
+ #[test]
+ fn method_taking_mutable_slice() {
+ insta::assert_display_snapshot!(rustfmt_code(
+ &gen_bridge(parse_quote! {
+ mod ffi {
+ struct Foo {}
+
+ impl Foo {
+ pub fn fill_slice(s: &mut [f64]) {
+ unimplemented!()
+ }
+ }
+ }
+ })
+ .to_token_stream()
+ .to_string()
+ ));
+ }
+
+ #[test]
+ fn mod_with_enum() {
+ insta::assert_display_snapshot!(rustfmt_code(
+ &gen_bridge(parse_quote! {
+ mod ffi {
+ enum Abc {
+ A,
+ B = 123,
+ }
+
+ impl Abc {
+ pub fn do_something(&self) {
+ unimplemented!()
+ }
+ }
+ }
+ })
+ .to_token_stream()
+ .to_string()
+ ));
+ }
+
+ #[test]
+ fn mod_with_writeable_result() {
+ insta::assert_display_snapshot!(rustfmt_code(
+ &gen_bridge(parse_quote! {
+ mod ffi {
+ struct Foo {}
+
+ impl Foo {
+ pub fn to_string(&self, to: &mut DiplomatWriteable) -> Result<(), ()> {
+ unimplemented!()
+ }
+ }
+ }
+ })
+ .to_token_stream()
+ .to_string()
+ ));
+ }
+
+ #[test]
+ fn mod_with_rust_result() {
+ insta::assert_display_snapshot!(rustfmt_code(
+ &gen_bridge(parse_quote! {
+ mod ffi {
+ struct Foo {}
+
+ impl Foo {
+ pub fn bar(&self) -> Result<(), ()> {
+ unimplemented!()
+ }
+ }
+ }
+ })
+ .to_token_stream()
+ .to_string()
+ ));
+ }
+
+ #[test]
+ fn multilevel_borrows() {
+ insta::assert_display_snapshot!(rustfmt_code(
+ &gen_bridge(parse_quote! {
+ mod ffi {
+ #[diplomat::opaque]
+ struct Foo<'a>(&'a str);
+
+ #[diplomat::opaque]
+ struct Bar<'b, 'a: 'b>(&'b Foo<'a>);
+
+ struct Baz<'x, 'y> {
+ foo: &'y Foo<'x>,
+ }
+
+ impl<'a> Foo<'a> {
+ pub fn new(x: &'a str) -> Box<Foo<'a>> {
+ unimplemented!()
+ }
+
+ pub fn get_bar<'b>(&'b self) -> Box<Bar<'b, 'a>> {
+ unimplemented!()
+ }
+
+ pub fn get_baz<'b>(&'b self) -> Baz<'b, 'a> {
+ Bax { foo: self }
+ }
+ }
+ }
+ })
+ .to_token_stream()
+ .to_string()
+ ));
+ }
+
+ #[test]
+ fn self_params() {
+ insta::assert_display_snapshot!(rustfmt_code(
+ &gen_bridge(parse_quote! {
+ mod ffi {
+ #[diplomat::opaque]
+ struct RefList<'a> {
+ data: &'a i32,
+ next: Option<Box<Self>>,
+ }
+
+ impl<'b> RefList<'b> {
+ pub fn extend(&mut self, other: &Self) -> Self {
+ unimplemented!()
+ }
+ }
+ }
+ })
+ .to_token_stream()
+ .to_string()
+ ));
+ }
+
+ #[test]
+ fn cfged_method() {
+ insta::assert_display_snapshot!(rustfmt_code(
+ &gen_bridge(parse_quote! {
+ mod ffi {
+ struct Foo {}
+
+ impl Foo {
+ #[cfg(feature = "foo")]
+ pub fn bar(s: u8) {
+ unimplemented!()
+ }
+ }
+ }
+ })
+ .to_token_stream()
+ .to_string()
+ ));
+
+ insta::assert_display_snapshot!(rustfmt_code(
+ &gen_bridge(parse_quote! {
+ mod ffi {
+ struct Foo {}
+
+ #[cfg(feature = "bar")]
+ impl Foo {
+ #[cfg(feature = "foo")]
+ pub fn bar(s: u8) {
+ unimplemented!()
+ }
+ }
+ }
+ })
+ .to_token_stream()
+ .to_string()
+ ));
+ }
+
+ #[test]
+ fn cfgd_struct() {
+ insta::assert_display_snapshot!(rustfmt_code(
+ &gen_bridge(parse_quote! {
+ mod ffi {
+ #[diplomat::opaque]
+ #[cfg(feature = "foo")]
+ struct Foo {}
+ #[cfg(feature = "foo")]
+ impl Foo {
+ pub fn bar(s: u8) {
+ unimplemented!()
+ }
+ }
+ }
+ })
+ .to_token_stream()
+ .to_string()
+ ));
+ }
+}
diff --git a/third_party/rust/diplomat/src/snapshots/diplomat__tests__cfgd_struct.snap b/third_party/rust/diplomat/src/snapshots/diplomat__tests__cfgd_struct.snap
new file mode 100644
index 0000000000..4328309170
--- /dev/null
+++ b/third_party/rust/diplomat/src/snapshots/diplomat__tests__cfgd_struct.snap
@@ -0,0 +1,23 @@
+---
+source: macro/src/lib.rs
+expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n #[diplomat :: opaque] #[cfg(feature = \"foo\")] struct Foo {}\n #[cfg(feature = \"foo\")] impl Foo\n { pub fn bar(s : u8) { unimplemented! () } }\n }\n }).to_token_stream().to_string())"
+---
+mod ffi {
+ #[cfg(feature = "foo")]
+ struct Foo {}
+ #[cfg(feature = "foo")]
+ impl Foo {
+ pub fn bar(s: u8) {
+ unimplemented!()
+ }
+ }
+ #[no_mangle]
+ #[cfg(feature = "foo")]
+ extern "C" fn Foo_bar(s: u8) {
+ Foo::bar(s)
+ }
+ #[no_mangle]
+ #[cfg(feature = "foo")]
+ extern "C" fn Foo_destroy(this: Box<Foo>) {}
+}
+
diff --git a/third_party/rust/diplomat/src/snapshots/diplomat__tests__cfged_method-2.snap b/third_party/rust/diplomat/src/snapshots/diplomat__tests__cfged_method-2.snap
new file mode 100644
index 0000000000..b026704b8d
--- /dev/null
+++ b/third_party/rust/diplomat/src/snapshots/diplomat__tests__cfged_method-2.snap
@@ -0,0 +1,24 @@
+---
+source: macro/src/lib.rs
+expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n struct Foo {} #[cfg(feature = \"bar\")] impl Foo\n {\n #[cfg(feature = \"foo\")] pub fn bar(s : u8)\n { unimplemented! () }\n }\n }\n }).to_token_stream().to_string())"
+---
+mod ffi {
+ #[repr(C)]
+ struct Foo {}
+ #[cfg(feature = "bar")]
+ impl Foo {
+ #[cfg(feature = "foo")]
+ pub fn bar(s: u8) {
+ unimplemented!()
+ }
+ }
+ #[no_mangle]
+ #[cfg(feature = "foo")]
+ #[cfg(feature = "bar")]
+ extern "C" fn Foo_bar(s: u8) {
+ Foo::bar(s)
+ }
+ #[no_mangle]
+ extern "C" fn Foo_destroy(this: Box<Foo>) {}
+}
+
diff --git a/third_party/rust/diplomat/src/snapshots/diplomat__tests__cfged_method.snap b/third_party/rust/diplomat/src/snapshots/diplomat__tests__cfged_method.snap
new file mode 100644
index 0000000000..bc4adbb73f
--- /dev/null
+++ b/third_party/rust/diplomat/src/snapshots/diplomat__tests__cfged_method.snap
@@ -0,0 +1,22 @@
+---
+source: macro/src/lib.rs
+expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n struct Foo {} impl Foo\n {\n #[cfg(feature = \"foo\")] pub fn bar(s : u8)\n { unimplemented! () }\n }\n }\n }).to_token_stream().to_string())"
+---
+mod ffi {
+ #[repr(C)]
+ struct Foo {}
+ impl Foo {
+ #[cfg(feature = "foo")]
+ pub fn bar(s: u8) {
+ unimplemented!()
+ }
+ }
+ #[no_mangle]
+ #[cfg(feature = "foo")]
+ extern "C" fn Foo_bar(s: u8) {
+ Foo::bar(s)
+ }
+ #[no_mangle]
+ extern "C" fn Foo_destroy(this: Box<Foo>) {}
+}
+
diff --git a/third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_mutable_slice.snap b/third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_mutable_slice.snap
new file mode 100644
index 0000000000..01fea65c2e
--- /dev/null
+++ b/third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_mutable_slice.snap
@@ -0,0 +1,20 @@
+---
+source: macro/src/lib.rs
+expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n struct Foo {} impl Foo\n { pub fn fill_slice(s : & mut [f64]) { unimplemented! () } }\n }\n }).to_token_stream().to_string())"
+---
+mod ffi {
+ #[repr(C)]
+ struct Foo {}
+ impl Foo {
+ pub fn fill_slice(s: &mut [f64]) {
+ unimplemented!()
+ }
+ }
+ #[no_mangle]
+ extern "C" fn Foo_fill_slice(s_diplomat_data: *mut f64, s_diplomat_len: usize) {
+ Foo::fill_slice(unsafe { core::slice::from_raw_parts_mut(s_diplomat_data, s_diplomat_len) })
+ }
+ #[no_mangle]
+ extern "C" fn Foo_destroy(this: Box<Foo>) {}
+}
+
diff --git a/third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_mutable_str.snap b/third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_mutable_str.snap
new file mode 100644
index 0000000000..bebbcf8455
--- /dev/null
+++ b/third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_mutable_str.snap
@@ -0,0 +1,26 @@
+---
+source: macro/src/lib.rs
+expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n struct Foo {} impl Foo\n {\n pub fn make_uppercase(s : & mut str) { unimplemented! () }\n }\n }\n }).to_token_stream().to_string())"
+---
+mod ffi {
+ #[repr(C)]
+ struct Foo {}
+ impl Foo {
+ pub fn make_uppercase(s: &mut str) {
+ unimplemented!()
+ }
+ }
+ #[no_mangle]
+ extern "C" fn Foo_make_uppercase(s_diplomat_data: *mut u8, s_diplomat_len: usize) {
+ Foo::make_uppercase(unsafe {
+ core::str::from_utf8_mut(core::slice::from_raw_parts_mut(
+ s_diplomat_data,
+ s_diplomat_len,
+ ))
+ .unwrap()
+ })
+ }
+ #[no_mangle]
+ extern "C" fn Foo_destroy(this: Box<Foo>) {}
+}
+
diff --git a/third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_slice.snap b/third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_slice.snap
new file mode 100644
index 0000000000..94daec2af5
--- /dev/null
+++ b/third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_slice.snap
@@ -0,0 +1,20 @@
+---
+source: macro/src/lib.rs
+expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n struct Foo {} impl Foo\n { pub fn from_slice(s : & [f64]) { unimplemented! () } }\n }\n }).to_token_stream().to_string())"
+---
+mod ffi {
+ #[repr(C)]
+ struct Foo {}
+ impl Foo {
+ pub fn from_slice(s: &[f64]) {
+ unimplemented!()
+ }
+ }
+ #[no_mangle]
+ extern "C" fn Foo_from_slice(s_diplomat_data: *const f64, s_diplomat_len: usize) {
+ Foo::from_slice(unsafe { core::slice::from_raw_parts(s_diplomat_data, s_diplomat_len) })
+ }
+ #[no_mangle]
+ extern "C" fn Foo_destroy(this: Box<Foo>) {}
+}
+
diff --git a/third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_str.snap b/third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_str.snap
new file mode 100644
index 0000000000..7e736e5599
--- /dev/null
+++ b/third_party/rust/diplomat/src/snapshots/diplomat__tests__method_taking_str.snap
@@ -0,0 +1,23 @@
+---
+source: macro/src/lib.rs
+expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n struct Foo {} impl Foo\n { pub fn from_str(s : & str) { unimplemented! () } }\n }\n }).to_token_stream().to_string())"
+---
+mod ffi {
+ #[repr(C)]
+ struct Foo {}
+ impl Foo {
+ pub fn from_str(s: &str) {
+ unimplemented!()
+ }
+ }
+ #[no_mangle]
+ extern "C" fn Foo_from_str(s_diplomat_data: *const u8, s_diplomat_len: usize) {
+ Foo::from_str(unsafe {
+ core::str::from_utf8(core::slice::from_raw_parts(s_diplomat_data, s_diplomat_len))
+ .unwrap()
+ })
+ }
+ #[no_mangle]
+ extern "C" fn Foo_destroy(this: Box<Foo>) {}
+}
+
diff --git a/third_party/rust/diplomat/src/snapshots/diplomat__tests__mod_with_enum.snap b/third_party/rust/diplomat/src/snapshots/diplomat__tests__mod_with_enum.snap
new file mode 100644
index 0000000000..723e17d786
--- /dev/null
+++ b/third_party/rust/diplomat/src/snapshots/diplomat__tests__mod_with_enum.snap
@@ -0,0 +1,23 @@
+---
+source: macro/src/lib.rs
+expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n enum Abc { A, B = 123, } impl Abc\n { pub fn do_something(& self) { unimplemented! () } }\n }\n }).to_token_stream().to_string())"
+---
+mod ffi {
+ #[repr(C)]
+ enum Abc {
+ A,
+ B = 123,
+ }
+ impl Abc {
+ pub fn do_something(&self) {
+ unimplemented!()
+ }
+ }
+ #[no_mangle]
+ extern "C" fn Abc_do_something(this: &Abc) {
+ this.do_something()
+ }
+ #[no_mangle]
+ extern "C" fn Abc_destroy(this: Box<Abc>) {}
+}
+
diff --git a/third_party/rust/diplomat/src/snapshots/diplomat__tests__mod_with_rust_result.snap b/third_party/rust/diplomat/src/snapshots/diplomat__tests__mod_with_rust_result.snap
new file mode 100644
index 0000000000..1eeb28808a
--- /dev/null
+++ b/third_party/rust/diplomat/src/snapshots/diplomat__tests__mod_with_rust_result.snap
@@ -0,0 +1,20 @@
+---
+source: macro/src/lib.rs
+expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n struct Foo {} impl Foo\n {\n pub fn bar(& self) -> Result < (), () >\n { unimplemented! () }\n }\n }\n }).to_token_stream().to_string())"
+---
+mod ffi {
+ #[repr(C)]
+ struct Foo {}
+ impl Foo {
+ pub fn bar(&self) -> Result<(), ()> {
+ unimplemented!()
+ }
+ }
+ #[no_mangle]
+ extern "C" fn Foo_bar(this: &Foo) -> diplomat_runtime::DiplomatResult<(), ()> {
+ this.bar().into()
+ }
+ #[no_mangle]
+ extern "C" fn Foo_destroy(this: Box<Foo>) {}
+}
+
diff --git a/third_party/rust/diplomat/src/snapshots/diplomat__tests__mod_with_writeable_result.snap b/third_party/rust/diplomat/src/snapshots/diplomat__tests__mod_with_writeable_result.snap
new file mode 100644
index 0000000000..870193052f
--- /dev/null
+++ b/third_party/rust/diplomat/src/snapshots/diplomat__tests__mod_with_writeable_result.snap
@@ -0,0 +1,25 @@
+---
+source: macro/src/lib.rs
+expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n struct Foo {} impl Foo\n {\n pub fn to_string(& self, to : & mut DiplomatWriteable) ->\n Result < (), () > { unimplemented! () }\n }\n }\n }).to_token_stream().to_string())"
+---
+mod ffi {
+ #[repr(C)]
+ struct Foo {}
+ impl Foo {
+ pub fn to_string(&self, to: &mut DiplomatWriteable) -> Result<(), ()> {
+ unimplemented!()
+ }
+ }
+ #[no_mangle]
+ extern "C" fn Foo_to_string(
+ this: &Foo,
+ to: &mut diplomat_runtime::DiplomatWriteable,
+ ) -> diplomat_runtime::DiplomatResult<(), ()> {
+ let ret = this.to_string(to);
+ to.flush();
+ ret.into()
+ }
+ #[no_mangle]
+ extern "C" fn Foo_destroy(this: Box<Foo>) {}
+}
+
diff --git a/third_party/rust/diplomat/src/snapshots/diplomat__tests__multilevel_borrows.snap b/third_party/rust/diplomat/src/snapshots/diplomat__tests__multilevel_borrows.snap
new file mode 100644
index 0000000000..f662608096
--- /dev/null
+++ b/third_party/rust/diplomat/src/snapshots/diplomat__tests__multilevel_borrows.snap
@@ -0,0 +1,45 @@
+---
+source: macro/src/lib.rs
+expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n #[diplomat :: opaque] struct Foo < 'a > (& 'a str) ;\n #[diplomat :: opaque] struct Bar < 'b, 'a : 'b >\n (& 'b Foo < 'a >) ; struct Baz < 'x, 'y >\n { foo : & 'y Foo < 'x >, } impl < 'a > Foo < 'a >\n {\n pub fn new(x : & 'a str) -> Box < Foo < 'a >>\n { unimplemented! () } pub fn get_bar < 'b > (& 'b self) ->\n Box < Bar < 'b, 'a >> { unimplemented! () } pub fn get_baz <\n 'b > (& 'b self) -> Baz < 'b, 'a > { Bax { foo : self } }\n }\n }\n }).to_token_stream().to_string())"
+---
+mod ffi {
+ struct Foo<'a>(&'a str);
+ struct Bar<'b, 'a: 'b>(&'b Foo<'a>);
+ #[repr(C)]
+ struct Baz<'x, 'y> {
+ foo: &'y Foo<'x>,
+ }
+ impl<'a> Foo<'a> {
+ pub fn new(x: &'a str) -> Box<Foo<'a>> {
+ unimplemented!()
+ }
+ pub fn get_bar<'b>(&'b self) -> Box<Bar<'b, 'a>> {
+ unimplemented!()
+ }
+ pub fn get_baz<'b>(&'b self) -> Baz<'b, 'a> {
+ Bax { foo: self }
+ }
+ }
+ #[no_mangle]
+ extern "C" fn Bar_destroy<'b, 'a: 'b>(this: Box<Bar<'b, 'a>>) {}
+ #[no_mangle]
+ extern "C" fn Baz_destroy<'x: 'y, 'y>(this: Box<Baz<'x, 'y>>) {}
+ #[no_mangle]
+ extern "C" fn Foo_new<'a>(x_diplomat_data: *const u8, x_diplomat_len: usize) -> Box<Foo<'a>> {
+ Foo::new(unsafe {
+ core::str::from_utf8(core::slice::from_raw_parts(x_diplomat_data, x_diplomat_len))
+ .unwrap()
+ })
+ }
+ #[no_mangle]
+ extern "C" fn Foo_get_bar<'a: 'b, 'b>(this: &'b Foo<'a>) -> Box<Bar<'b, 'a>> {
+ this.get_bar()
+ }
+ #[no_mangle]
+ extern "C" fn Foo_get_baz<'a: 'b, 'b>(this: &'b Foo<'a>) -> Baz<'b, 'a> {
+ this.get_baz()
+ }
+ #[no_mangle]
+ extern "C" fn Foo_destroy<'a>(this: Box<Foo<'a>>) {}
+}
+
diff --git a/third_party/rust/diplomat/src/snapshots/diplomat__tests__self_params.snap b/third_party/rust/diplomat/src/snapshots/diplomat__tests__self_params.snap
new file mode 100644
index 0000000000..6b931197ab
--- /dev/null
+++ b/third_party/rust/diplomat/src/snapshots/diplomat__tests__self_params.snap
@@ -0,0 +1,22 @@
+---
+source: macro/src/lib.rs
+expression: "rustfmt_code(&gen_bridge(parse_quote! {\n mod ffi\n {\n #[diplomat :: opaque] struct RefList < 'a >\n { data : & 'a i32, next : Option < Box < Self >>, } impl <\n 'b > RefList < 'b >\n {\n pub fn extend(& mut self, other : & Self) -> Self\n { unimplemented! () }\n }\n }\n }).to_token_stream().to_string())"
+---
+mod ffi {
+ struct RefList<'a> {
+ data: &'a i32,
+ next: Option<Box<Self>>,
+ }
+ impl<'b> RefList<'b> {
+ pub fn extend(&mut self, other: &Self) -> Self {
+ unimplemented!()
+ }
+ }
+ #[no_mangle]
+ extern "C" fn RefList_extend<'b>(this: &mut RefList<'b>, other: &RefList<'b>) -> RefList<'b> {
+ this.extend(other)
+ }
+ #[no_mangle]
+ extern "C" fn RefList_destroy<'a>(this: Box<RefList<'a>>) {}
+}
+
diff --git a/third_party/rust/diplomat/src/transparent_convert.rs b/third_party/rust/diplomat/src/transparent_convert.rs
new file mode 100644
index 0000000000..9ac8bf64e7
--- /dev/null
+++ b/third_party/rust/diplomat/src/transparent_convert.rs
@@ -0,0 +1,30 @@
+use quote::quote;
+use syn::*;
+
+pub fn gen_transparent_convert(s: ItemStruct) -> proc_macro2::TokenStream {
+ let mut fields = s.fields.iter();
+ let field1 = if let Some(field1) = fields.next() {
+ &field1.ty
+ } else {
+ panic!("#[diplomat::transparent_convert] only allowed on structs with a single field")
+ };
+
+ if fields.next().is_some() {
+ panic!("#[diplomat::transparent_convert] only allowed on structs with a single field")
+ }
+ let struct_name = &s.ident;
+ let (impl_generics, ty_generics, _) = s.generics.split_for_impl();
+ let mut impl_generics: Generics = parse_quote!(#impl_generics);
+ let custom_lifetime: GenericParam = parse_quote!('transparent_convert_outer);
+ impl_generics.params.push(custom_lifetime);
+ quote! {
+ impl #impl_generics #struct_name #ty_generics {
+ // can potentially add transparent_convert_owned, _mut later
+ pub(crate) fn transparent_convert(from: &'transparent_convert_outer #field1) -> &'transparent_convert_outer Self {
+ unsafe {
+ &*(from as *const #field1 as *const Self)
+ }
+ }
+ }
+ }
+}