lang_generator/
lib.rs

1mod iso639;
2
3fn to_camel_case(name: &str) -> String {
4    use heck::ToUpperCamelCase;
5
6    let cleaned = name
7        .split("(")
8        .next()
9        .unwrap_or(name)
10        .replace(',', "")
11        .replace('-', " ")
12        .replace('_', " ");
13
14    cleaned.trim().to_upper_camel_case()
15}
16
17use std::collections::HashMap;
18
19use proc_macro::TokenStream;
20use quote::{format_ident, quote};
21use syn::Ident;
22
23use crate::iso639::read_csv;
24
25#[proc_macro]
26pub fn generate_language(_: TokenStream) -> TokenStream {
27    let mut tokens = proc_macro2::TokenStream::new();
28
29    let csv = read_csv().unwrap();
30    let mut from_tos: HashMap<String, Vec<(Ident, Option<String>)>> = HashMap::new();
31    let variant_idents = csv
32        .into_iter()
33        .map(|map| {
34            let name = map.get("name").as_ref().unwrap().as_ref().unwrap();
35            let enum_name = format_ident!("{}", to_camel_case(name));
36            for (key, item) in map {
37                from_tos
38                    .entry(key.to_owned())
39                    .or_default()
40                    .push((enum_name.clone(), item));
41            }
42            enum_name
43        })
44        .collect::<Vec<_>>();
45    tokens.extend(quote! {
46        #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
47        pub enum Language {
48            #(#variant_idents),*
49        }
50
51        impl Language {
52            pub fn all() -> Vec<Self> {
53                vec![#(Self::#variant_idents),*]
54            }
55        }
56    });
57
58    for (key, variants) in from_tos {
59        let fn_from = format_ident!("from_{}", key.replace("/", "").replace("-", "_"));
60        let fn_to = format_ident!("to_{}", key.replace("/", "").replace("-", "_"));
61
62        let from_arms = variants
63            .iter()
64            .filter_map(|v| v.1.clone().map(|vv| (v.0.clone(), vv)))
65            .map(|(variant, val)| {
66                quote! { #val => Some(Language::#variant), }
67            });
68
69        let to_arms = variants
70            .iter()
71            .filter_map(|v| v.1.clone().map(|vv| (v.0.clone(), vv)))
72            .map(|(variant, val)| {
73                quote! { Language::#variant => Some(#val), }
74            });
75
76        let impl_block = quote! {
77            impl Language {
78                pub fn #fn_from(s: &str) -> Option<Self> {
79                    match s {
80                        #(#from_arms)*
81                        _ => None,
82                    }
83                }
84
85                pub fn #fn_to(&self) -> Option<&'static str> {
86                    match self {
87                        #(#to_arms)*
88                        _ => None,
89                    }
90                }
91            }
92        };
93        tokens.extend(impl_block);
94    }
95
96    tokens.into()
97}