miko-macros 0.3.10

Macros for miko
Documentation
use quote::quote;
use std::collections::HashMap;
use syn::parse::{Parse, ParseStream};
use syn::{Expr, ExprLit, Lit, LitStr, Meta, Token};

#[derive(Debug, Clone)]
pub struct StrAttrMap {
    pub map: HashMap<String, String>,
    pub default: Option<String>,
}
impl StrAttrMap {
    pub fn new() -> Self {
        Self {
            map: HashMap::new(),
            default: None,
        }
    }
}

impl Parse for StrAttrMap {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        Ok(StrAttrMap::from_parse_stream(input))
    }
}

impl StrAttrMap {
    pub fn from_parse_stream(input: ParseStream) -> Self {
        let mut _default = None;
        let mut map = HashMap::new();
        while !input.is_empty() {
            if input.peek(LitStr) {
                let s: LitStr = input.parse().unwrap();
                _default = Some(s.value());
            } else {
                let meta: Meta = input.parse().unwrap();
                match meta {
                    Meta::NameValue(nvmeta) => {
                        let ident = nvmeta.path.get_ident().unwrap();
                        if let Expr::Lit(ExprLit {
                            lit: Lit::Str(str), ..
                        }) = nvmeta.value
                        {
                            map.insert(ident.to_string(), str.value());
                        }
                    }
                    Meta::Path(path) => {
                        let ident = path.get_ident().unwrap().to_string();
                        map.insert(ident.clone(), ident);
                    }
                    _ => {}
                }
            }
            if input.peek(Token![,]) {
                let _comma: Token![,] = input.parse().unwrap();
            }
        }
        Self {
            map,
            default: _default,
        }
    }

    pub fn get(&self, key: &str) -> Option<&String> {
        self.map.get(key)
    }
    pub fn get_or_default(&self, key: &str) -> Option<String> {
        self.map
            .get(key)
            .map(|s| s.to_string())
            .or(self.default.clone())
    }

    pub fn to_token_stream(&self) -> proc_macro2::TokenStream {
        let mut tokens = proc_macro2::TokenStream::new();
        if let Some(ref default) = self.default {
            tokens.extend(quote! {
                #default
            });
            if !self.map.is_empty() {
                tokens.extend(quote! { , });
            }
        }
        let entries: Vec<_> = self.map.iter().collect();
        for (idx, (key, value)) in entries.iter().enumerate() {
            let key_ident = syn::Ident::new(key, proc_macro2::Span::call_site());
            tokens.extend(quote! {
                #key_ident = #value
            });
            if idx < entries.len() - 1 {
                tokens.extend(quote! { , });
            }
        }

        tokens
    }
}