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::{RawTokenStream, parse_t};
9
10fn hashmap_to_tokens(
11 h: &HashMap<String, String>,
12 default_locale: &str,
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::trim_literal(l));
59 }
60 TokenTree::Punct(p) => {
61 s.push(p.as_char());
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: &[(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!(
106 "Key not found: {}. Available keys: {:?}",
107 key,
108 get_locale().keys()
109 ),
110 };
111 let replacement = replacement_to_tokens(&replacement);
112 let (values, names, default_index) = hashmap_to_tokens(map, &default_locale());
113 quote!(
114 {
115 #values;
116 let mut value = values[match format!("{}", #locale).as_str() {
117 #(#names)*
118 _ => #default_index,
119 }].to_string();
120 #replacement
121 value
122 }
123 )
124 .into()
125}
126
127#[proc_macro]
128pub fn all(_item: RawTokenStream) -> RawTokenStream {
129 let all = load::get_locale();
130 let mut all_coll = Vec::new();
131 for (key, map) in all {
132 let mut map_coll = Vec::new();
133 for (key, value) in map {
134 let key = Literal::string(key);
135 let value = Literal::string(value);
136 map_coll.push(quote! {
137 map.insert(#key, #value);
138 });
139 }
140 let key = Literal::string(key);
141 all_coll.push(quote! {
142 let mut map = std::collections::HashMap::<&'static str, &'static str>::new();
143 #(#map_coll)*
144 all.insert(#key, map);
145 });
146 }
147 quote!(
148 {
149 let mut all = std::collections::HashMap::new();
150 #(
151 #all_coll
152 )*
153 all
154 }
155 )
156 .into()
157}
158
159#[proc_macro]
161pub fn loc(_item: RawTokenStream) -> RawTokenStream {
162 let loc = load::get_locale_list();
163 let loc = loc.iter().map(|x| Literal::string(x));
164 quote!(
165 [
166 #(
167 #loc
168 ),*
169 ]
170 )
171 .into()
172}