bk2d-macro 0.3.0

Useful macro for impl BK2D
Documentation
use std::collections::HashMap;

use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{
    parse::{Parse, ParseStream},
    punctuated::Punctuated,
    Ident, Lit, MetaNameValue, Result, Token,
};

pub struct Setting {
    pub settings: HashMap<String, String>,
}

macro_rules! default {
    ($map:ident, $key:expr) => {
        $map.entry($key.to_string())
            .or_insert_with(|| $key.to_string())
    };
    ($map:ident, $key:expr, $value:expr) => {
        $map.entry($key.to_string())
            .or_insert_with(|| $value.to_string())
    };
}

impl Parse for Setting {
    fn parse(input: ParseStream) -> Result<Self> {
        let a: Punctuated<_, Token![,]> = input.parse_terminated(MetaNameValue::parse)?;
        let mut map: HashMap<_, _> = a
            .iter()
            .map(|nv| {
                let lit = if let Lit::Str(s) = &nv.lit {
                    s.value()
                } else {
                    panic!()
                };
                (nv.ident.to_string(), lit)
            })
            .collect();
        default!(map, "players");
        default!(map, "check_player");
        Ok(Setting { settings: map })
    }
}

pub fn use_default(settings: Setting) -> TokenStream {
    let name = Ident::new(&settings.settings["players"], Span::call_site());
    let check_player = Ident::new(&settings.settings["check_player"], Span::call_site());
    quote! {
        fn add_player(&mut self, p: Player) -> Result<()> {
            self.#check_player(&p)?;
            self.players.add(p);
            Ok(())
        }

        fn current_player(&self) -> &String {
            self.#name.current_ident()
        }

        fn next_player(&mut self) -> &String {
            self.stage = 0;
            if self.#name.next_player() == 0 {
                self.round += 1;
            }
            self.#name.current_ident()
        }

        fn is_start(&self) -> bool {
            self.#name.is_start()
        }
    }
}