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}