zng_ext_l10n_proc_macros/
lang.rs
1use proc_macro2::TokenStream;
2use quote::{quote, quote_spanned};
3use std::str::FromStr;
4use syn::{Ident, LitStr, ext::IdentExt, parse::Parse, parse_macro_input};
5
6pub fn expand(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
7 let Input { unic_langid, lang } = parse_macro_input!(input as Input);
8
9 let (raw, e_span) = match lang {
10 LangInput::LitStr(s) => (s.value(), s.span()),
11 LangInput::Ident(i) => (i.to_string(), i.span()),
12 };
13
14 let r = match unic_langid::LanguageIdentifier::from_str(&raw) {
15 Ok(lang) => {
16 let (lang, script, region, variants) = lang.into_parts();
17
18 let lang: Option<u64> = lang.into();
19 let lang = if let Some(lang) = lang {
20 quote!(unsafe { #unic_langid::subtags::Language::from_raw_unchecked(#lang) })
21 } else {
22 quote!(std::default::Default::default())
23 };
24
25 let script = if let Some(script) = script {
26 let script: u32 = script.into();
27 quote!(Some(unsafe { #unic_langid::subtags::Script::from_raw_unchecked(#script) }))
28 } else {
29 quote!(None)
30 };
31
32 let region = if let Some(region) = region {
33 let region: u32 = region.into();
34 quote!(Some(unsafe { #unic_langid::subtags::Region::from_raw_unchecked(#region) }))
35 } else {
36 quote!(None)
37 };
38
39 let variants = if !variants.is_empty() {
40 let v: Vec<_> = variants
41 .iter()
42 .map(|v| {
43 let variant: u64 = v.into();
44 quote!(unsafe { #unic_langid::subtags::Variant::from_raw_unchecked(#variant) })
45 })
46 .collect();
47 quote!(Some(Box::new([#(#v,)*])))
48 } else {
49 quote!(None)
50 };
51
52 quote! {
53 #unic_langid::LanguageIdentifier::from_raw_parts_unchecked(#lang, #script, #region, #variants)
54 }
55 }
56 Err(e) => {
57 let e = match e {
58 unic_langid::LanguageIdentifierError::Unknown => "unknown error".to_owned(),
59 unic_langid::LanguageIdentifierError::ParserError(e) => e.to_string(),
60 };
61 quote_spanned! {e_span=>
62 compile_error!(#e)
63 }
64 }
65 };
66
67 r.into()
68}
69
70struct Input {
71 unic_langid: TokenStream,
72 lang: LangInput,
73}
74impl Parse for Input {
75 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
76 Ok(Input {
77 unic_langid: non_user_braced!(input, "unic_langid").parse().unwrap(),
78 lang: non_user_braced!(input, "lang").parse().unwrap(),
79 })
80 }
81}
82
83enum LangInput {
84 LitStr(LitStr),
85 Ident(Ident),
86}
87impl Parse for LangInput {
88 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
89 if input.peek(Ident::peek_any) {
90 Ok(LangInput::Ident(input.parse()?))
91 } else {
92 Ok(LangInput::LitStr(input.parse()?))
93 }
94 }
95}