use std::collections::HashMap;
use crate::group::Group;
use heck::{ToKebabCase, ToSnakeCase};
use icu_locid::{locale, Locale};
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::punctuated::Punctuated;
pub fn derive_enum(
group: Group,
ident: syn::Ident,
variants: Punctuated<syn::Variant, syn::token::Comma>,
) -> TokenStream {
let mut idents = Vec::with_capacity(variants.len());
let mut extra_messages = Vec::with_capacity(variants.len());
let mut canonical_messages = Vec::with_capacity(variants.len());
for variant in variants {
let kebab_case_ident = variant.ident.to_string().to_kebab_case();
let variant_ident = variant.ident;
idents.push(match variant.fields {
syn::Fields::Named(fields) => {
let field_idents = fields
.named
.iter()
.map(|field| field.ident.as_ref().unwrap())
.map(|ident| format_ident!("{}", ident.to_string().to_snake_case()));
quote!(#variant_ident { #(#field_idents),* })
}
syn::Fields::Unnamed(_) => todo!(),
syn::Fields::Unit => quote!(#variant_ident),
});
extra_messages.push(group.message(&kebab_case_ident));
canonical_messages.push(group.canonical_message(&kebab_case_ident));
}
quote! {
impl #ident {
fn localize(&self, locales: &[&::icu_locid::Locale], plural_rules: &::icu_plurals::PluralRules) -> String {
for locale in locales {
match self {
#(Self::#idents => #extra_messages),*
}
}
match self {
#(Self::#idents => #canonical_messages),*
}
}
}
}
}
pub fn attribute_groups(path_literal: syn::LitStr) -> Group {
let manifest_root = std::env::var("CARGO_MANIFEST_DIR").unwrap();
let attribute_glob = path_literal.value();
let mut resources = HashMap::new();
let glob = wax::Glob::new(&attribute_glob).unwrap();
for potential_entry in glob.walk(&manifest_root) {
let entry = potential_entry.unwrap();
let captured_locale = entry.matched().get(1).unwrap();
let stripped_locale = captured_locale.strip_suffix('/').unwrap_or(captured_locale);
let locale = Locale::try_from_bytes(stripped_locale.as_bytes()).unwrap();
let fluent_contents = std::fs::read_to_string(entry.path()).unwrap();
let resource = fluent_syntax::parser::parse(fluent_contents).unwrap();
resources.insert(locale, resource);
}
Group::new(locale!("en-US"), resources)
}
pub fn localize(
path: syn::LitStr,
ident: syn::Ident,
variants: Punctuated<syn::Variant, syn::token::Comma>,
) -> TokenStream {
let groups = attribute_groups(path);
derive_enum(groups, ident, variants)
}