use proc_macro2::{Literal, Span, TokenStream};
use quote::quote;
use syn::{
parse::{Parse, ParseStream},
DeriveInput, Ident, Result, Token,
};
use crate::parse::{check_unittype, get_data_struct};
pub(crate) fn get_strlookup_type(input: &DeriveInput) -> Result<()> {
let data_struct = get_data_struct(input)?;
check_unittype(data_struct)
}
pub(crate) fn get_strlookup_hashmap_impl(
name: &Ident,
key_type: Ident,
store_type: Ident,
hashmap_size: Literal,
) -> TokenStream {
quote! {
struct #name;
impl stringid::StrLookupHashMap for #name {
type Key = #key_type;
type StrStore = #store_type;
fn hashmap() -> &'static std::sync::RwLock<cityhasher::HashMap<Self::Key, &'static str>> {
static STRING_TABLE: once_cell::sync::Lazy<
std::sync::RwLock<cityhasher::HashMap<#key_type, &'static str>>,
> = once_cell::sync::Lazy::new(|| create_hash_map::<#key_type>(#hashmap_size));
&STRING_TABLE
}
}
}
}
#[derive(Debug)]
pub(crate) struct StrLookupHashMap {
pub key_type: Ident,
pub store_type: Ident,
pub hashmap_size: Literal,
}
impl Parse for StrLookupHashMap {
fn parse(input: ParseStream) -> syn::Result<Self> {
let mut key_type: Option<Ident> = None;
let mut store_type: Option<Ident> = None;
let mut hashmap_size: Option<Literal> = None;
while !input.is_empty() {
if input.peek(Token![,]) {
let _eq: Token![,] = input.parse()?;
} else {
let ident: Ident = input.parse()?;
match ident.to_string().as_str() {
"key" => {
let msg = concat!(
"`key` must be used with a assigned value.\n",
"For example:\n",
" key = u32\n"
);
key_type = Some(parse_attr_value(input, &ident, msg)?);
}
"store" => {
let msg = concat!(
"`store` must be used with a assigned value .\n",
"For example:\n",
" store = BufferStrStore\n\n"
);
store_type = Some(parse_attr_value(input, &ident, msg)?);
}
"size" => {
let msg = concat!(
"`size` must be used with a assigned value .\n",
"For example:\n",
" size = 128\n\n"
);
hashmap_size = Some(parse_attr_value(input, &ident, msg)?);
}
_ => {
let msg = format!("Unknown attribute `{ident}`, `key` and `store` are the only autorized and wanted attribute");
return Err(syn::Error::new(ident.span(), msg));
}
}
}
}
Ok(StrLookupHashMap {
key_type: key_type.ok_or(syn::Error::new(
Span::call_site(),
"a 'key' type should be specified",
))?,
store_type: store_type.ok_or(syn::Error::new(
Span::call_site(),
"a 'store' type should be specified",
))?,
hashmap_size: hashmap_size.ok_or(syn::Error::new(
Span::call_site(),
"a 'size' for the hashmap should be specified",
))?,
})
}
}
fn parse_attr_value<T: Parse>(
input: &syn::parse::ParseBuffer<'_>,
ident: &Ident,
msg: &str,
) -> Result<T> {
let _eq: Token![=] = input.parse().or(Err(syn::Error::new(ident.span(), msg)))?;
input.parse()
}