1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
//! Set of macros designed to help creating and implementing `WebAssembly` APIs and bindings,
//! designed to run in the Ark environment.
//!
//! See also the [private Ark
//! documentation](https://ark.embark.dev/internals/creating-apis/index.html) for details of how
//! things work, motivation behind this crate, as well as thorough examples of what it can do.
//!
//! # The problem
//!
//! Implementing `WebAssembly` FFI (foreign function interfaces) by hand is tedious and doesn't
//! allow using idiomatic Rust, like taking or returning non-plain types (user structs, enum, etc.
//! but also idiomatic Rust types like `Result`/`String` and so on and so forth).
//!
//! # The solution
//!
//! This crate provides macros that are designed to work together to solve this particular
//! problem, and allow the usage of high-level patterns in APIs.
//!
//! ## User-side `ark_bindgen` macro
//!
//! This macro applies on a `mod` defined in a Rust file that is to be compiled to `WebAssembly`.
//! The output of this macro depends on whether it's running on the host or not:
//!
//! - On the host, this will create an `HostShim` trait, that contains a few static internal
//! methods, as well as methods mirroring the extern C function declarations, using high-level,
//! idiomatic Rust patterns. This macro can be partially implemented automatically with the
//! `host_exports` macro, described below.
//! - In user code, this will also create the trait as well as a `safe` Rust `mod` containing
//! functions with the same signatures as in the declaration.
//!
//! Here are the different kind of declarations one can do with it:
//!
//! // TODO reinclude, see also #4516
//! //```rust,ignore
//! //#![doc = include_str!("../../api-ffi/src/ffi/example_automatic.rs")]
//! //```
//!
//! ## Host-side `host_export` macro
//!
//! This macro applied on impl blocks for the `HostShim` trait generated by the above macro. An end
//! user only needs to implement the `_shim` functions in the trait, and get the rest implemented
//! for free by this macro.
//!
//! // TODO reinclude, see also #4516
//! //```rust,ignore
//! //#![doc = include_str!("../../../components/module-host/src/host_api/examples/automatic_macro.rs")]
//! //```
//!
//! ## `ark_test` macro
//!
//! This macro is designed to replicate the behavior of `#[test]` in Rust modules compiled to
//! `WebAssembly`.

// BEGIN - Embark standard lints v0.4
// do not change or add/remove here, but one can add exceptions after this section
// for more info see: <https://github.com/EmbarkStudios/rust-ecosystem/issues/59>
#![deny(unsafe_code)]
#![warn(
    clippy::all,
    clippy::await_holding_lock,
    clippy::char_lit_as_u8,
    clippy::checked_conversions,
    clippy::dbg_macro,
    clippy::debug_assert_with_mut_call,
    clippy::doc_markdown,
    clippy::empty_enum,
    clippy::enum_glob_use,
    clippy::exit,
    clippy::expl_impl_clone_on_copy,
    clippy::explicit_deref_methods,
    clippy::explicit_into_iter_loop,
    clippy::fallible_impl_from,
    clippy::filter_map_next,
    clippy::float_cmp_const,
    clippy::fn_params_excessive_bools,
    clippy::if_let_mutex,
    clippy::implicit_clone,
    clippy::imprecise_flops,
    clippy::inefficient_to_string,
    clippy::invalid_upcast_comparisons,
    clippy::large_types_passed_by_value,
    clippy::let_unit_value,
    clippy::linkedlist,
    clippy::lossy_float_literal,
    clippy::macro_use_imports,
    clippy::manual_ok_or,
    clippy::map_err_ignore,
    clippy::map_flatten,
    clippy::map_unwrap_or,
    clippy::match_on_vec_items,
    clippy::match_same_arms,
    clippy::match_wildcard_for_single_variants,
    clippy::mem_forget,
    clippy::mismatched_target_os,
    clippy::mut_mut,
    clippy::mutex_integer,
    clippy::needless_borrow,
    clippy::needless_continue,
    clippy::option_option,
    clippy::path_buf_push_overwrite,
    clippy::ptr_as_ptr,
    clippy::ref_option_ref,
    clippy::rest_pat_in_fully_bound_structs,
    clippy::same_functions_in_if_condition,
    clippy::semicolon_if_nothing_returned,
    clippy::string_add_assign,
    clippy::string_add,
    clippy::string_lit_as_bytes,
    clippy::string_to_string,
    clippy::todo,
    clippy::trait_duplication_in_bounds,
    clippy::unimplemented,
    clippy::unnested_or_patterns,
    clippy::unused_self,
    clippy::useless_transmute,
    clippy::verbose_file_reads,
    clippy::zero_sized_map_values,
    future_incompatible,
    nonstandard_style,
    rust_2018_idioms
)]
// END - Embark standard lints v0.4
// BEGIN - Ark-specific lints
#![warn(clippy::flat_map_option)]
#![deny(clippy::disallowed_method)]
// END - Ark-specific lints

// crate-specific exceptions:
#![allow()]

use proc_macro::TokenStream;
use quote::quote;

mod bindgen;
mod host_exports;
mod test;
mod utils;

/// Replaces an impl block of an `HostShim` trait (generated by the `ark_bindgen` macro) by an
/// augmented one that automatically implements some functions, making it more ergonomic and
/// pleasant to use.
///
/// Note this macro is designed to be used on the **host**.
///
/// See the crate description for examples of what you can do with it.
#[proc_macro_attribute]
pub fn host_exports(_args: TokenStream, input: TokenStream) -> TokenStream {
    let mut item = syn::parse_macro_input!(input as host_exports::Item);
    match host_exports::expand(&mut item) {
        Ok(_) => TokenStream::from(quote!(#item)),
        Err(e) => e.to_compile_error().into(),
    }
}

/// Replaces a `mod` block by another one that augments type/struct/external function declarations
/// to make them more concise, ergonomic and pleasant to use.
///
/// In particular, generates an `HostShim` trait that is implemented partially automatically thanks
/// to the `host_exports` macro above.
///
/// Note this macro is designed to be used in `WebAssembly` code (creating a `safe` Rust module
/// that can used directly) *and* on the host (creating the trait that will be implemented by the
/// above macro).
///
/// See the crate description for examples of what you can do with it.
///
/// This unfortunately can't be applied at module scope yet; see also
/// <https://github.com/rust-lang/rust/issues/54726>.
#[proc_macro_attribute]
pub fn ark_bindgen(args: TokenStream, input: TokenStream) -> TokenStream {
    let args = syn::parse_macro_input!(args as bindgen::Args);
    let mut item = syn::parse_macro_input!(input as bindgen::Item);

    match bindgen::expand(&mut item, args) {
        Ok(_) => TokenStream::from(quote!(#item)),
        Err(e) => e.to_compile_error().into(),
    }
}

/// Macro emulating the behavior of the standard `#[test]` macro, but for module code compiled to
/// `WebAssembly`. Such a test can then be executed with ark using `ark module test`.
///
/// ```rust,ignore
/// #[ark_test]
/// fn makes_a_sad() {
///     assert_eq!(1, 2, "me am gud in nummers");
/// }
/// ```
#[proc_macro_attribute]
pub fn ark_test(
    attr: proc_macro::TokenStream,
    body: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
    match test::try_ark_test(attr, body) {
        Ok(result) => result.into(),
        Err(err) => err.to_compile_error().into(),
    }
}