summaryrefslogtreecommitdiffstats
path: root/third_party/rust/diplomat
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/rust/diplomat
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/diplomat')
-rw-r--r--third_party/rust/diplomat/.cargo-checksum.json1
-rw-r--r--third_party/rust/diplomat/Cargo.toml55
-rw-r--r--third_party/rust/diplomat/LICENSE-APACHE201
-rw-r--r--third_party/rust/diplomat/LICENSE-MIT27
-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
19 files changed, 1383 insertions, 0 deletions
diff --git a/third_party/rust/diplomat/.cargo-checksum.json b/third_party/rust/diplomat/.cargo-checksum.json
new file mode 100644
index 0000000000..e68087c073
--- /dev/null
+++ b/third_party/rust/diplomat/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"Cargo.toml":"42a489a0e146bba3fe0d2c1e9abc24f8f24164404b88c687a53c575864ac7a7e","LICENSE-APACHE":"639c20c7f14fb122750d5ad1a6cfb116d9bf8d103e709ee40949e5a12a731666","LICENSE-MIT":"3337fe6e4a3830ad87c23cb9d6d750f9a1e5c45efc08de9c76c1a207fc6966c4","src/enum_convert.rs":"c0068cb8b563043030186cd9a0be6a4eac1a5f1761fe3646a99528e6d3dc5f54","src/lib.rs":"e05eb7285e68c64ee6936868cb3b39bfac041ee722bb4ecd6fb892fdf8bcff06","src/snapshots/diplomat__tests__cfgd_struct.snap":"2874497e83ba3f43541d90869bfa428973c5b23df7fec2826e187d530f1a620b","src/snapshots/diplomat__tests__cfged_method-2.snap":"b2ff2801b5f1e08bf660c63a5b8763cf99326e0b9d13cdcc9ae82532825727b2","src/snapshots/diplomat__tests__cfged_method.snap":"b478d2d14e01209b45032b092bf91a2068f08f704b2395bc3ebd2ada21141077","src/snapshots/diplomat__tests__method_taking_mutable_slice.snap":"eafa335a999e416a044d32106e096ef57b7f41fb2f74b03d4adce13e7179b03d","src/snapshots/diplomat__tests__method_taking_mutable_str.snap":"e4c65337861a78b3c9762545fcdbccc1169bab9183ff750fc467a5367dba9c56","src/snapshots/diplomat__tests__method_taking_slice.snap":"eb2d7d00381ddef71411c170ce27d2490acfe9322bc3de397fe1cedcedeeee7b","src/snapshots/diplomat__tests__method_taking_str.snap":"92aa38d8618f0d52d5bc967a8d67a87f4b9cdc4f8651c624a9cc7b73033dbaa4","src/snapshots/diplomat__tests__mod_with_enum.snap":"fc225e910aa1afe496eb8d4da4342894f7786c53e12725b2f70018cf5230dddc","src/snapshots/diplomat__tests__mod_with_rust_result.snap":"48e30564d2cf0477db7062c58842b264d0cfec1d635e7dbaf12c12a2e9f1ab31","src/snapshots/diplomat__tests__mod_with_writeable_result.snap":"6ddbc34dbf7d362366cddda70c87a2f25d7ba0821ccbab90b5a79748f1064593","src/snapshots/diplomat__tests__multilevel_borrows.snap":"708fb54c2c3b8498aac83c20af891e12212874b94691fd734fd57f500ce54666","src/snapshots/diplomat__tests__self_params.snap":"1f6652799973e4afa751afebd6306e71ea439640cc1cae9848c5757d060bf699","src/transparent_convert.rs":"dde901986a6709a21f359596e85bc4fd009bb645c79b698d5af8e2a603996ac4"},"package":"a31672b3ebc3c7866c3c98726f7a9a5ac8f13962e77d3c8225f6be49a7b8c5f2"} \ No newline at end of file
diff --git a/third_party/rust/diplomat/Cargo.toml b/third_party/rust/diplomat/Cargo.toml
new file mode 100644
index 0000000000..af5ef28fa6
--- /dev/null
+++ b/third_party/rust/diplomat/Cargo.toml
@@ -0,0 +1,55 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2018"
+name = "diplomat"
+version = "0.7.0"
+authors = [
+ "Shadaj Laddad <shadaj@users.noreply.github.com>",
+ "Manish Goregaokar <manishsmail@gmail.com>",
+ "Quinn Okabayashi <QnnOkabayashi@users.noreply.github.com>",
+]
+description = "The diplomat FFI generation macro"
+documentation = "https://docs.rs/diplomat_core/"
+keywords = [
+ "ffi",
+ "codegen",
+]
+categories = ["development-tools"]
+license = "MIT OR Apache-2.0"
+repository = "https://github.com/rust-diplomat/diplomat"
+
+[lib]
+path = "src/lib.rs"
+proc-macro = true
+
+[dependencies.diplomat_core]
+version = "0.7.0"
+
+[dependencies.proc-macro2]
+version = "1.0.27"
+
+[dependencies.quote]
+version = "1.0"
+
+[dependencies.syn]
+version = "2.0"
+features = [
+ "full",
+ "extra-traits",
+]
+
+[dev-dependencies.insta]
+version = "1.7.1"
+
+[dev-dependencies.tempfile]
+version = "3.2.0"
diff --git a/third_party/rust/diplomat/LICENSE-APACHE b/third_party/rust/diplomat/LICENSE-APACHE
new file mode 100644
index 0000000000..05fcffa855
--- /dev/null
+++ b/third_party/rust/diplomat/LICENSE-APACHE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+Copyright 2022 The Diplomat Developers
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/third_party/rust/diplomat/LICENSE-MIT b/third_party/rust/diplomat/LICENSE-MIT
new file mode 100644
index 0000000000..cf62c31bc7
--- /dev/null
+++ b/third_party/rust/diplomat/LICENSE-MIT
@@ -0,0 +1,27 @@
+MIT License
+
+Copyright (c) 2022 The Diplomat Developers
+
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
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)
+ }
+ }
+ }
+ }
+}