golem_rust_macro/lib.rs
1// Copyright 2024 Golem Cloud
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use proc_macro::TokenStream;
16
17use syn::*;
18
19use crate::transaction::golem_operation_impl;
20
21mod expand;
22mod transaction;
23mod wit_gen;
24
25/// Derives `From<>` And `Into<>` typeclasses for wit-bindgen generated data types (e.g. `WitPerson`)
26/// and custom domain data types (e.g. `Person`). So it's possible to write code like this:
27/// ```ignore
28/// let person = Person {
29/// name: "John Doe".to_owned(),
30/// age: 42,
31/// };
32///
33/// let converted: WitPerson = person.into();
34/// ```
35///
36/// `#[wit_type_name(WitPerson)]` is optional. Defines name of the wit-bindgen generated data type. Default is name of this data type prepanded with 'Wit'.
37///
38/// `#[rename_field("age2")]` is optional. Anotates field and specify field name in the other data type, in case it's different.
39///
40/// # Example:
41/// ```
42/// pub struct WitPerson {
43/// pub title: String,
44/// pub age: i32,
45/// }
46///
47///
48/// #[derive(golem_rust_macro::WIT_From_Into)]
49/// #[wit_type_name(WitPerson)]
50/// pub struct Person {
51///
52/// #[rename_field("title")]
53/// pub name: String,
54///
55/// pub age: i32
56/// }
57/// ```
58#[proc_macro_derive(WIT_From_Into, attributes(wit_type_name, rename_field))]
59pub fn derive(input: TokenStream) -> TokenStream {
60 let mut input = parse_macro_input!(input as DeriveInput);
61
62 expand::expand_wit(&mut input)
63 .unwrap_or_else(syn::Error::into_compile_error)
64 .into()
65}
66
67/// Annotates a module with `#[golem_rust_macro::create_wit_file]` and generates WIT file in the root of your project.
68/// Supports enums, structs, traits and alias types.
69///
70/// # Example:
71/// ```
72/// #[golem_rust_macro::create_wit_file("auction_app.wit")]
73/// mod auction_app {
74///
75/// struct BidderId {
76/// bidder_id: String,
77/// }
78///
79/// struct AuctionId {
80/// auction_id: String,
81/// }
82///
83/// struct Auction {
84/// auction_id: Option<AuctionId>,
85/// name: String,
86/// description: String,
87/// starting_price: f32,
88/// deadline: Deadline,
89/// }
90///
91/// enum BidResult {
92/// Failure(String),
93/// Success,
94/// }
95///
96/// type Deadline = u64;
97///
98/// trait AuctionService {
99/// fn initialize(auction: Auction);
100///
101/// fn bid(bidder_id: BidderId, price: f32) -> BidResult;
102///
103/// fn close_auction() -> Option<BidderId>;
104///
105/// fn create_bidder(name: String, address: String) -> BidderId;
106///
107/// fn create_auction(
108/// name: String,
109/// description: String,
110/// starting_price: f32,
111/// deadline: u64,
112/// ) -> AuctionId;
113///
114/// fn get_auctions() -> Vec<Auction>;
115/// }
116/// }
117/// ```
118///
119/// File `auction_app.wit` is then created with the following content.
120///
121/// ```ignore
122/// package auction:app
123///
124/// interface api {
125///
126/// record bidder-id {
127/// bidder-id: string,
128/// }
129///
130/// record auction-id {
131/// auction-id: string,
132/// }
133///
134/// record auction {
135/// auction-id: option<auction-id>,
136/// name: string,
137/// description: string,
138/// starting-price: float32,
139/// deadline: deadline,
140/// }
141///
142/// variant bid-result {
143/// failure(string),
144/// success
145/// }
146///
147/// type deadline = u64
148///
149/// initialize: func(auction: auction)
150///
151/// bid: func(bidder-id: bidder-id, price: float32) -> bid-result
152///
153/// close-auction: func() -> option<bidder-id>
154///
155/// create-bidder: func(name: string, address: string) -> bidder-id
156///
157/// create-auction: func(name: string, description: string, starting-price: float32, deadline: u64) -> auction-id
158///
159/// get-auctions: func() -> list<auction>
160/// }
161///
162/// world golem-service {
163/// export api
164/// }
165/// ```
166#[proc_macro_attribute]
167pub fn create_wit_file(_attr: TokenStream, item: TokenStream) -> TokenStream {
168 let item_moved = item.clone();
169
170 let mut input = parse_macro_input!(item_moved as ItemMod);
171
172 let file_name_result = syn::parse2::<syn::Lit>(_attr.into())
173 .map_or_else(|_| {
174 Ok("generated.wit".to_owned())
175 },
176 |literal| {
177 match literal {
178 syn::Lit::Str(lit) => {
179 let mut n = lit.value();
180 if n.ends_with(".wit") {
181 Ok(n)
182 } else {
183 n.push_str(".wit");
184 Ok(n)
185 }
186 },
187 _ => Err(syn::Error::new(literal.span(), "If you want to specify name of the generated file, please input is as a String, otherwise do not input any attributes. \n Generated file will be 'generated.wit'")),
188 }
189 });
190
191 file_name_result
192 .and_then(|file_name| wit_gen::generate_witfile(&mut input, file_name).to_owned())
193 .unwrap_or_else(syn::Error::into_compile_error)
194 .into()
195}
196
197/// Defines a function as an `Operation` that can be used in transactions
198#[proc_macro_attribute]
199pub fn golem_operation(attr: TokenStream, item: TokenStream) -> TokenStream {
200 golem_operation_impl(attr, item)
201}