deq_macros/
lib.rs

1extern crate deq_core;
2extern crate proc_macro;
3extern crate quote;
4extern crate syn;
5use proc_macro::TokenStream;
6use quote::quote;
7use syn::{parse::Parser, parse_macro_input, DeriveInput};
8
9/// Dervies the transaction code
10#[proc_macro_derive(Transaction)]
11pub fn transaction_macro_derive(input: TokenStream) -> TokenStream {
12    // Construct a representation of Rust code as a syntax tree
13    // that we can manipulate
14    let ast = syn::parse(input).unwrap();
15
16    // Build the trait implementation
17    impl_transaction_macro(&ast)
18}
19
20/// Adds transaction data to a struct
21/// must be applied to a struct
22/// Transaction data should not be modified by anything but
23/// the commit, revert and begin methods
24#[proc_macro_attribute]
25pub fn transaction_fields(_args: TokenStream, input: TokenStream) -> TokenStream {
26    let mut ast = parse_macro_input!(input as DeriveInput);
27    match &mut ast.data {
28        syn::Data::Struct(ref mut struct_data) => {
29            match &mut struct_data.fields {
30                syn::Fields::Named(fields) => {
31                    fields.named.push(
32                        syn::Field::parse_named
33                            .parse2(quote! { pub transaction_data: TransactionData<Self> })
34                            .unwrap(),
35                    );
36                }
37                _ => (),
38            }
39
40            return quote! {
41                #ast
42            }
43            .into();
44        }
45        _ => panic!("`transactions_fields` must be used with structs"),
46    }
47}
48
49fn impl_transaction_macro(ast: &syn::DeriveInput) -> TokenStream {
50    let name = &ast.ident;
51    let gen = quote! {
52        impl Transaction for #name {
53            fn begin(&mut self) {
54                self.transaction_data.t.push(self.clone());
55            }
56
57            fn commit(&mut self) -> Result<(), TransactionError> {
58                match self.transaction_data.t.pop() {
59                    Some(_) => Ok(()),
60                    None => Err(TransactionError::new(TransactionErrorType::TransactionNotStarted))
61                }
62            }
63
64            fn revert(&mut self) -> Result<(), TransactionError> {
65                match self.transaction_data.t.pop() {
66                    Some(prev) => {
67                        *self = prev;
68                        return Ok(());
69                    },
70                    None => Err(TransactionError::new(TransactionErrorType::TransactionNotStarted))
71                }
72            }
73
74            fn len(&self) -> usize {
75                self.transaction_data.t.len()
76            }
77        }
78    };
79    gen.into()
80}