localization_macros/
lib.rs

1mod load;
2mod t;
3use std::collections::HashMap;
4
5use load::{default_locale, get_locale};
6use proc_macro2::{Literal, TokenStream, TokenTree};
7use quote::quote;
8use t::{parse_t, RawTokenStream};
9
10fn hashmap_to_tokens(
11    h: &HashMap<String, String>,
12    default_locale: String,
13) -> (TokenStream, Vec<TokenStream>, usize) {
14    let mut locales = Vec::new();
15    let mut values = Vec::new();
16    let mut default_index = 0;
17    for (i, (key, value)) in h.iter().enumerate() {
18        if key == &default_locale {
19            default_index = i + 1;
20        }
21        let key = Literal::string(key);
22        locales.push(quote! {
23            #key => #i,
24        });
25        let value = Literal::string(value);
26        values.push(value);
27    }
28    if default_index == 0 {
29        panic!("Default locale not found");
30    }
31    (
32        quote! {
33            let values = [
34                #(#values),*
35            ];
36        },
37        locales,
38        default_index - 1,
39    )
40}
41
42fn append(l: Literal) -> Literal {
43    let s = l.to_string();
44    let mut s = s[1..s.len() - 1].to_string();
45    s.insert(0, '{');
46    s.insert(0, '{');
47    s.insert(s.len(), '}');
48    s.insert(s.len(), '}');
49    Literal::string(&s)
50}
51
52fn into_literal(ts: &TokenStream) -> Literal {
53    let ts = ts.clone().into_iter();
54    let mut s = String::new();
55    for item in ts {
56        match item {
57            TokenTree::Literal(l) => {
58                s.push_str(&t::literal_trim(l));
59            }
60            TokenTree::Punct(p) => {
61                s.push_str(&p.to_string());
62            }
63            TokenTree::Ident(i) => {
64                s.push_str(&i.to_string());
65            }
66            TokenTree::Group(g) => {
67                s.push_str(&g.to_string());
68            }
69        }
70    }
71    Literal::string(&s)
72}
73
74fn replacement_to_tokens(r: &Vec<(TokenStream, Option<TokenStream>)>) -> TokenStream {
75    let mut tokens = TokenStream::new();
76    for (key, value) in r {
77        let value = if let Some(value) = value { value } else { key };
78        let key = append(into_literal(key));
79        tokens.extend(quote! {
80            value = value.replace(
81                #key,
82                &format!("{}", #value)
83            );
84        });
85    }
86    tokens
87}
88
89/// Use the localization thing
90/// # Example
91/// ```
92/// use localization::t;
93/// fn example() {
94///   let name = "John";
95///   let age = 42;
96///   let s = t!("ja-JP","default:hello", name, age);
97///   println!("{}", s);
98/// }
99/// ```
100#[proc_macro]
101pub fn t(item: RawTokenStream) -> RawTokenStream {
102    let (locale, key, replacement) = parse_t(item);
103    let map = match get_locale().get(&key) {
104        Some(map) => map,
105        None => panic!("Key not found: {}", key),
106    };
107    let replacement = replacement_to_tokens(&replacement);
108    let (values, names, default_index) = hashmap_to_tokens(map, default_locale());
109    quote!(
110        {
111            #values;
112            let mut value = values[match format!("{}",#locale).as_str() {
113                #(#names)*
114                _ => #default_index,
115            }].to_string();
116            #replacement
117            value
118        }
119    )
120    .into()
121}
122
123#[proc_macro]
124pub fn all(_item: RawTokenStream) -> RawTokenStream {
125    let all = load::get_locale();
126    let mut all_coll = Vec::new();
127    for (key, map) in all {
128        let mut map_coll = Vec::new();
129        for (key, value) in map {
130            let key = Literal::string(key);
131            let value = Literal::string(value);
132            map_coll.push(quote! {
133                map.insert(#key, #value);
134            });
135        }
136        let key = Literal::string(key);
137        all_coll.push(quote! {
138            let mut map = std::collections::HashMap::<&'static str, &'static str>::new();
139            #(#map_coll)*
140            all.insert(#key, map);
141        });
142    }
143    quote!(
144        {
145            let mut all = std::collections::HashMap::new();
146            #(
147                #all_coll
148            )*
149            all
150        }
151    )
152    .into()
153}
154
155/// get all localization
156#[proc_macro]
157pub fn loc(_item: RawTokenStream) -> RawTokenStream {
158    let loc = load::get_locale_list();
159    let loc = loc.iter().map(|x| Literal::string(x));
160    quote!(
161        [
162            #(
163                #loc
164            ),*
165        ]
166    )
167    .into()
168}