1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#![recursion_limit = "128"]

extern crate proc_macro;
extern crate proc_macro2;
#[macro_use]
extern crate syn;
#[macro_use]
extern crate quote;

use crate::proc_macro::TokenStream;
use proc_macro2::Span;

#[proc_macro_attribute]
pub fn moves(
    _args: TokenStream,
    input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as syn::ItemTrait);
    let items = &input.items;

    let method_names = items.iter().filter_map(|itm| match itm {
            syn::TraitItem::Method(m) => {
                Some(m.sig.ident.to_string())
            },
            _ => None
    }).collect::<Vec<String>>();
    let matchers = method_names.iter().map(|name| {
        let ident = syn::Ident::new(name, Span::call_site());
        let quoted = quote! {
            if move_type == #name {
                return MovesImpl::#ident(state, game_move.player_id, &game_move.args);
            }
        };
        quoted
    });
    let names = method_names.clone();

    TokenStream::from(quote! {
        #[macro_use]
        extern crate lazy_static;
        
        pub struct MovesImpl;
        impl MovesImpl {
            #(#items)*
            fn list_moves() -> Vec<&'static str> {
                vec![#(#names)*]
            }
            fn make_move(state: &mut UserState<State>, game_move: &Move)
                         -> Result<(), Box<Error>> {
                let move_type = &game_move.move_type;
                #(#matchers)*
                Err(Box::new(GameError::InvalidMove(game_move.clone())))
            }
        }
    })
}

#[proc_macro_attribute]
pub fn flow(
    _args: TokenStream,
    input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as syn::ItemTrait);
    let items = &input.items;

    TokenStream::from(quote! {
        mod external {
            extern {
                pub fn debug(str_ptr: *const u8, str_len: u32);
            }
        }

        fn debug(msg: &str) {
            unsafe { external::debug(msg.as_ptr(), msg.len() as u32); }
        }

        pub struct FlowImpl {}
        impl UserFlow<State> for FlowImpl {
            #(#items)*
            fn list_moves (&self) -> Vec<&'static str> {
                MovesImpl::list_moves()
            }

            // This is the main entry point into the game, which dispatches moves to their handlers.
            fn make_move(&self, state: &mut UserState<State>, game_move: &Move)
                         -> Result<(), Box<Error>> {
                MovesImpl::make_move(state, game_move)
            }
        }

        pub struct Game;
        impl StoreFactory for Game {
            fn create(&self, player_id: u16, players: Vec<u16>, multiplayer: bool, server: bool, initialize: bool, seed: Option<u128>) -> Box<Store> {
                let num_players = players.len() as u16;
                Box::new(StoreImpl::new(
                    oasis_game_core::Game {
                        player_limit: num_players, 
                        seed,
                        flow: FlowImpl {}
                    },
                    player_id,
                    players,
                    multiplayer,
                    server,
                    initialize
                ))
            }
        }
    })
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        assert_eq!(2 + 2, 4);
    }
}