//! Procedural macros powering `fai-module-sdk`. //! //! This crate exists only to host the `#[fai_module]` attribute. //! Module authors should depend on `fai-module-sdk` (which //! re-exports the macro), not on this crate directly. use proc_macro::TokenStream; use quote::quote; use syn::{ItemFn, parse_macro_input}; /// The frozen WIT contract, embedded at proc-macro compile time. /// The platform repo and this repo both ship a SHA-256 snapshot /// test that fails if these copies drift. const WIT_INLINE: &str = include_str!("../../../wit/world.wit"); /// Wrap a function with all the WASM module boilerplate. /// /// The annotated function must have the signature: /// /// ```ignore /// fn invoke(ctx: Context, inputs: Inputs) -> Result /// ``` /// /// The macro generates the `wit_bindgen::generate!` invocation, /// the `Guest` implementation, the WIT-to-SDK conversions, and /// the `wit_bindgen::export!` call. The function can be named /// anything — only its signature matters. #[proc_macro_attribute] pub fn fai_module(_attrs: TokenStream, input: TokenStream) -> TokenStream { let user_fn = parse_macro_input!(input as ItemFn); let user_fn_name = user_fn.sig.ident.clone(); let wit_literal = WIT_INLINE; let expanded = quote! { #user_fn // The WASM glue (wit_bindgen::generate!, Guest impl, export!) // is only emitted for the wasm32 target. Host builds skip // it so module crates can use crate-type = ["cdylib", "rlib"] // and run integration tests via `cargo test` without // dragging the wit_bindgen runtime onto the host target. #[cfg(target_arch = "wasm32")] #[doc(hidden)] mod __fai_module_sdk_internal { #![allow( unsafe_op_in_unsafe_fn, unused_imports, dead_code, clippy::all, clippy::pedantic, clippy::nursery )] ::fai_module_sdk::__rt::wit_bindgen::generate!({ inline: #wit_literal, world: "module", runtime_path: "::fai_module_sdk::__rt::wit_bindgen::rt", }); use exports::fai::platform::invoke::Guest; use fai::platform::types::{ BytesValue as WitBytes, FileRef as WitFileRef, InvocationContext as WitContext, InvocationError as WitError, Payload as WitPayload, }; struct Component; impl Guest for Component { fn invoke( ctx: WitContext, inputs: ::std::vec::Vec<( ::std::string::String, WitPayload, )>, ) -> ::core::result::Result< ::std::vec::Vec<(::std::string::String, WitPayload)>, WitError, > { let sdk_ctx = ::fai_module_sdk::Context { invocation_id: ctx.invocation_id, capability_namespace: ctx.capability_namespace, capability_name: ctx.capability_name, deadline_ms: ctx.deadline_ms, }; let sdk_pairs: ::std::vec::Vec<( ::std::string::String, ::fai_module_sdk::Payload, )> = inputs .into_iter() .map(|(k, p)| (k, wit_payload_to_sdk(p))) .collect(); let sdk_inputs = ::fai_module_sdk::Inputs::__from_pairs(sdk_pairs); match super::#user_fn_name(sdk_ctx, sdk_inputs) { Ok(outputs) => { let pairs = outputs.__into_pairs(); let wit_pairs = pairs .into_iter() .map(|(k, p)| (k, sdk_payload_to_wit(p))) .collect(); Ok(wit_pairs) } Err(e) => Err(sdk_error_to_wit(e)), } } } fn wit_payload_to_sdk(p: WitPayload) -> ::fai_module_sdk::Payload { match p { WitPayload::Text(s) => ::fai_module_sdk::Payload::Text(s), WitPayload::Json(s) => ::fai_module_sdk::Payload::Json(s), WitPayload::Bytes(b) => ::fai_module_sdk::Payload::Bytes( ::fai_module_sdk::BytesValue { mime_type: b.mime_type, data: b.data, }, ), WitPayload::FileRef(f) => ::fai_module_sdk::Payload::FileRef( ::fai_module_sdk::FileRef { uri: f.uri, mime_type: f.mime_type, size_bytes: f.size_bytes, sha256: f.sha256, }, ), } } fn sdk_payload_to_wit(p: ::fai_module_sdk::Payload) -> WitPayload { match p { ::fai_module_sdk::Payload::Text(s) => WitPayload::Text(s), ::fai_module_sdk::Payload::Json(s) => WitPayload::Json(s), ::fai_module_sdk::Payload::Bytes(b) => WitPayload::Bytes(WitBytes { mime_type: b.mime_type, data: b.data, }), ::fai_module_sdk::Payload::FileRef(f) => WitPayload::FileRef(WitFileRef { uri: f.uri, mime_type: f.mime_type, size_bytes: f.size_bytes, sha256: f.sha256, }), } } fn sdk_error_to_wit(e: ::fai_module_sdk::ModuleError) -> WitError { match e { ::fai_module_sdk::ModuleError::InvalidInput(s) => { WitError::InvalidInput(s) } ::fai_module_sdk::ModuleError::PermissionDenied(s) => { WitError::PermissionDenied(s) } ::fai_module_sdk::ModuleError::ResourceExhausted(s) => { WitError::ResourceExhausted(s) } ::fai_module_sdk::ModuleError::DeadlineExceeded => { WitError::DeadlineExceeded } ::fai_module_sdk::ModuleError::Internal(s) => WitError::Internal(s), } } export!(Component); } }; TokenStream::from(expanded) }