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}