1extern crate proc_macro;
2extern crate syn;
3#[macro_use]
4extern crate quote;
5
6use proc_macro::TokenStream;
7use syn::parse::{Parse, ParseStream};
8use syn::punctuated::Punctuated;
9use syn::{braced, parse_macro_input, token, Field, Ident, Result, Token, Type};
10
11#[derive(Debug)]
12enum Item {
13 Struct(ExprStruct),
14}
15
16#[derive(Debug)]
17struct ExprStruct {
18 ident: Ident,
19 _brace_token: token::Brace,
20 fields: Punctuated<Field, Token![,]>,
21}
22
23impl Parse for Item {
24 fn parse(input: ParseStream) -> Result<Self> {
25 input.parse().map(Item::Struct)
26 }
33}
34
35impl Parse for ExprStruct {
36 fn parse(input: ParseStream) -> Result<Self> {
37 let content;
38 Ok(ExprStruct {
39 ident: input.parse()?,
40 _brace_token: braced!(content in input),
41 fields: content.parse_terminated(Field::parse_named, Token![,])?,
42 })
43 }
44}
45
46#[proc_macro]
67pub fn language_pack(tokens: TokenStream) -> TokenStream {
68 let input = parse_macro_input!(tokens as Item);
69 let expanded = match input {
70 Item::Struct(expr) => {
71 let language = expr.ident;
72 let language_label = language.to_string().to_lowercase();
73 let code_field = expr
74 .fields
75 .iter()
76 .find(|&f| *f.ident.as_ref().unwrap() == "code")
77 .unwrap();
78
79 let mapping_field = expr
80 .fields
81 .iter()
82 .find(|&f| *f.ident.as_ref().unwrap() == "mapping")
83 .unwrap();
84
85 let pre_processor_mapping_field = expr
86 .fields
87 .iter()
88 .find(|&f| *f.ident.as_ref().unwrap() == "pre_processor_mapping");
89
90 let reverse_specific_mapping_field = expr
91 .fields
92 .iter()
93 .find(|&f| *f.ident.as_ref().unwrap() == "reverse_specific_mapping");
94
95 let reverse_specific_pre_processor_mapping_field = expr
96 .fields
97 .iter()
98 .find(|&f| *f.ident.as_ref().unwrap() == "reverse_specific_pre_processor_mapping");
99
100 let code = match &code_field.ty {
101 Type::Path(type_path) => {
102 let code = &type_path.path.segments.first().unwrap().ident;
103 quote! { #code }
104 }
105 _ => panic!("Not a valid language code for language {}", language),
106 };
107
108 let mapping = match &mapping_field.ty {
109 Type::Path(type_path) => {
110 let mapping = &type_path.path.segments.first().unwrap().ident;
111 quote! { #mapping.iter().cloned().collect() }
112 }
113 _ => panic!("Not a valid mapping for language {}", language),
114 };
115
116 let pre_processor_mapping = if let Some(field) = pre_processor_mapping_field {
117 match &field.ty {
118 Type::Path(type_path) => {
119 let mapping = &type_path.path.segments.first().unwrap().ident;
120
121 quote! { Some(#mapping.iter().cloned().collect()) }
122 }
123 _ => panic!(
124 "Not a valid pre_processor_mapping for language {}",
125 language
126 ),
127 }
128 } else {
129 quote! { None }
130 };
131
132 let reverse_specific_mapping = if let Some(field) = reverse_specific_mapping_field {
133 match &field.ty {
134 Type::Path(type_path) => {
135 let mapping = &type_path.path.segments.first().unwrap().ident;
136
137 quote! { Some(#mapping.iter().cloned().collect()) }
138 }
139 _ => panic!(
140 "Not a valid pre_processor_mapping for language {}",
141 language
142 ),
143 }
144 } else {
145 quote! { None }
146 };
147
148 let reverse_specific_pre_processor_mapping =
149 if let Some(field) = reverse_specific_pre_processor_mapping_field {
150 match &field.ty {
151 Type::Path(type_path) => {
152 let mapping = &type_path.path.segments.first().unwrap().ident;
153
154 quote! { Some(#mapping.iter().cloned().collect()) }
155 }
156 _ => panic!(
157 "Not a valid pre_processor_mapping for language {}",
158 language
159 ),
160 }
161 } else {
162 quote! { None }
163 };
164
165 quote! {
166 use std::{convert::From, default:: Default};
167 use crate::transliterator::{Transliterator, TransliteratorBuilder};
168
169 #[derive(Clone, Debug)]
170 pub struct #language {
171 language: String,
172 code: String,
173 }
174
175 impl Default for #language {
176 fn default() -> Self {
177 Self {
178 language: #language_label.to_string(),
179 code: #code.to_string()
180 }
181 }
182 }
183
184 impl #language {
185 pub fn new() -> Self {
186 Default::default()
187 }
188 }
189
190 impl From<#language> for Transliterator {
191 fn from(language: #language) -> Self {
192 TransliteratorBuilder::default()
193 .language(language.language)
194 .code(language.code)
195 .mapping(#mapping)
196 .pre_processor_mapping(#pre_processor_mapping)
197 .reverse_specific_mapping(#reverse_specific_mapping)
198 .reverse_specific_pre_processor_mapping(#reverse_specific_pre_processor_mapping)
199 .build()
200 .unwrap()
201 }
202 }
203
204
205 }
206 }
207 };
208
209 TokenStream::from(expanded)
210}