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
mod load_locale; use proc_macro::TokenStream; use std::path::Path; use once_cell::unsync::Lazy; use load_locale::{find, load_locale, LocaleMap}; thread_local! { static LOCALE_MAP: Lazy<LocaleMap> = Lazy::new(load_locales_from_env); } fn load_locales_from_env() -> LocaleMap { let locale_path = option_env!("LOCALE_PATH"); if let Some(locale_path) = locale_path.map(|path| Path::new(path)) { let locale_path = if locale_path.is_relative() { find( Path::new(&std::env::var("OUT_DIR").unwrap_or_else(|_| String::from("./"))), locale_path, ) .unwrap() } else { locale_path.to_path_buf() }; if !locale_path.is_dir() { panic!( "locale path is not exists or not a directory! :{:?}", locale_path ); } load_locale(locale_path) } else { Default::default() } } #[proc_macro] pub fn check_language(code: TokenStream) -> TokenStream { let code = syn::parse_macro_input!(code as syn::LitStr).value(); LOCALE_MAP.with(|lm| { assert!(lm.contains_key(&code), "Locale {} does not exist!", &code); }); TokenStream::default() } #[proc_macro] pub fn check_field(field: TokenStream) -> TokenStream { let field = syn::parse_macro_input!(field as syn::LitStr).value(); LOCALE_MAP.with(|lm| { assert!( lm.values().all(|lsm| lsm.contains_key(&field)), "Field {} does not exist!", &field ); }); TokenStream::default() } #[proc_macro] pub fn build_match_func(_: TokenStream) -> TokenStream { let mut match1 = proc_macro2::TokenStream::new(); LOCALE_MAP.with(|locale_map| { for (code, lsm) in locale_map.iter() { let mut match2 = proc_macro2::TokenStream::new(); for (k, v) in lsm.iter() { match2.extend(quote::quote! { #k => Some((Cow::Borrowed(#v))), }); } match1.extend(quote::quote! { #code => { match field.as_ref() { #match2 _ => None } }, }); } }); (quote::quote! { #[inline(never)] pub fn _match_message<S>(lang_code: S, field: S) -> Option<Cow<'static, str>> where S: AsRef<str> { match lang_code.as_ref() { #match1 _ => None } } }) .into() }