localization_macros/
lib.rs1mod 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#[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#[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}