linera_witty_macros/
lib.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4//! # Linera Witty Macros
5//!
6//! This crate contains the procedural macros used by the `linera-witty` crate.
7
8#![deny(missing_docs)]
9
10mod util;
11mod wit_export;
12mod wit_import;
13mod wit_interface;
14mod wit_load;
15mod wit_store;
16mod wit_type;
17
18use proc_macro::TokenStream;
19use proc_macro2::Span;
20use proc_macro_error::{abort, proc_macro_error};
21use quote::{quote, ToTokens};
22#[cfg(with_wit_export)]
23use syn::ItemImpl;
24use syn::{parse_macro_input, Data, DeriveInput, Ident, ItemTrait};
25
26use self::util::{apply_specialization_attribute, AttributeParameters, Specializations};
27
28/// Derives `WitType` for a Rust type.
29///
30/// All fields in the type must also implement `WitType`.
31#[proc_macro_error]
32#[proc_macro_derive(WitType, attributes(witty, witty_specialize_with))]
33pub fn derive_wit_type(input: TokenStream) -> TokenStream {
34    let mut input = parse_macro_input!(input as DeriveInput);
35
36    let specializations = apply_specialization_attribute(&mut input);
37    let wit_name = wit_type::discover_wit_name(&input.attrs, &input.ident);
38
39    let body = match &input.data {
40        Data::Struct(struct_item) => wit_type::derive_for_struct(wit_name, &struct_item.fields),
41        Data::Enum(enum_item) => {
42            wit_type::derive_for_enum(&input.ident, wit_name, enum_item.variants.iter())
43        }
44        Data::Union(_union_item) => {
45            abort!(input.ident, "Can't derive `WitType` for `union`s")
46        }
47    };
48
49    derive_trait(
50        input,
51        specializations,
52        body,
53        Ident::new("WitType", Span::call_site()),
54    )
55}
56
57/// Derives `WitLoad` for the Rust type.
58///
59/// All fields in the type must also implement `WitLoad`.
60#[proc_macro_error]
61#[proc_macro_derive(WitLoad, attributes(witty, witty_specialize_with))]
62pub fn derive_wit_load(input: TokenStream) -> TokenStream {
63    let mut input = parse_macro_input!(input as DeriveInput);
64
65    let specializations = apply_specialization_attribute(&mut input);
66
67    let body = match &input.data {
68        Data::Struct(struct_item) => wit_load::derive_for_struct(&struct_item.fields),
69        Data::Enum(enum_item) => wit_load::derive_for_enum(&input.ident, enum_item.variants.iter()),
70        Data::Union(_union_item) => {
71            abort!(input.ident, "Can't derive `WitLoad` for `union`s")
72        }
73    };
74
75    derive_trait(
76        input,
77        specializations,
78        body,
79        Ident::new("WitLoad", Span::call_site()),
80    )
81}
82
83/// Derives `WitStore` for the Rust type.
84///
85/// All fields in the type must also implement `WitStore`.
86#[proc_macro_error]
87#[proc_macro_derive(WitStore, attributes(witty, witty_specialize_with))]
88pub fn derive_wit_store(input: TokenStream) -> TokenStream {
89    let mut input = parse_macro_input!(input as DeriveInput);
90
91    let specializations = apply_specialization_attribute(&mut input);
92
93    let body = match &input.data {
94        Data::Struct(struct_item) => wit_store::derive_for_struct(&struct_item.fields),
95        Data::Enum(enum_item) => {
96            wit_store::derive_for_enum(&input.ident, enum_item.variants.iter())
97        }
98        Data::Union(_union_item) => {
99            abort!(input.ident, "Can't derive `WitStore` for `union`s")
100        }
101    };
102
103    derive_trait(
104        input,
105        specializations,
106        body,
107        Ident::new("WitStore", Span::call_site()),
108    )
109}
110
111/// Derives a trait named `trait_name` with the specified `body`.
112///
113/// Contains the common code to extract and apply the type's generics for the trait implementation.
114fn derive_trait(
115    input: DeriveInput,
116    specializations: Specializations,
117    body: impl ToTokens,
118    trait_name: Ident,
119) -> TokenStream {
120    let (generic_parameters, type_generics, where_clause) =
121        specializations.split_generics_from(&input.generics);
122    let type_name = &input.ident;
123
124    quote! {
125        impl #generic_parameters #trait_name for #type_name #type_generics #where_clause {
126            #body
127        }
128    }
129    .into()
130}
131
132/// Generates a generic type from a trait.
133///
134/// The generic type has a type parameter for the Wasm guest instance to use, and allows calling
135/// functions that the instance exports through the trait's methods.
136#[proc_macro_error]
137#[proc_macro_attribute]
138pub fn wit_import(attribute: TokenStream, input: TokenStream) -> TokenStream {
139    let input = parse_macro_input!(input as ItemTrait);
140    let parameters = AttributeParameters::new(attribute);
141
142    wit_import::generate(input, parameters).into()
143}
144
145/// Registers an `impl` block's functions as callable host functions exported to guest Wasm
146/// modules.
147///
148/// The code generated depends on the enabled feature flags to determine which Wasm runtimes will
149/// be supported.
150#[cfg(with_wit_export)]
151#[proc_macro_error]
152#[proc_macro_attribute]
153pub fn wit_export(attribute: TokenStream, input: TokenStream) -> TokenStream {
154    let input = parse_macro_input!(input as ItemImpl);
155    let parameters = AttributeParameters::new(attribute);
156
157    wit_export::generate(&input, parameters).into()
158}