use super::plural_category::PluralCategory;
use proc_macro2::{Literal, TokenStream};
use quote::quote;
use std::collections::BTreeMap;
use std::str;
use unic_langid::LanguageIdentifier;
pub fn gen_fn(streams: BTreeMap<String, Vec<TokenStream>>, vr: &str) -> TokenStream {
let ignore_noncritical_errors = quote! {
#![allow(unused_variables, unused_parens)]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::float_cmp))]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::unreadable_literal))]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::nonminimal_bool))]
};
let use_statements = quote! {
use super::operands::PluralOperands;
use super::PluralCategory;
use unic_langid::LanguageIdentifier;
use tinystr::{TinyStr4, TinyStr8};
};
let plural_function = quote! { pub type PluralRule = fn(&PluralOperands) -> PluralCategory; };
let num: isize = vr.parse().unwrap();
let ver = Literal::u64_unsuffixed(num as u64);
let version = quote! { pub static CLDR_VERSION: usize = #ver; };
let head = quote! { #ignore_noncritical_errors #use_statements #plural_function #version };
let mut tokens = Vec::<TokenStream>::new();
for (pr_type, stream) in streams {
tokens.push(create_pr_type(&pr_type, stream));
}
let filling = quote! { #(#tokens)* };
let prs = quote! { #[cfg_attr(tarpaulin, skip)] #filling };
quote! { #head #prs }
}
fn create_pr_type(pr_type: &str, streams: Vec<TokenStream>) -> TokenStream {
let mut tokens = Vec::<TokenStream>::new();
let match_name = match pr_type {
"cardinal" => quote! { PRS_CARDINAL },
"ordinal" => quote! { PRS_ORDINAL },
_ => panic!("Unknown plural rule type"),
};
for func in &streams {
tokens.push(func.clone());
}
quote! { pub const #match_name: &[(LanguageIdentifier, PluralRule)] = &[ #(#tokens),* ]; }
}
fn create_return(cat: PluralCategory, exp: &TokenStream) -> TokenStream {
match cat {
PluralCategory::ZERO => quote! {if #exp { PluralCategory::ZERO } },
PluralCategory::ONE => quote! {if #exp { PluralCategory::ONE } },
PluralCategory::TWO => quote! {if #exp { PluralCategory::TWO } },
PluralCategory::FEW => quote! {if #exp { PluralCategory::FEW } },
PluralCategory::MANY => quote! {if #exp { PluralCategory::MANY } },
PluralCategory::OTHER => quote! { { PluralCategory::OTHER } },
}
}
pub fn gen_langid(id: &LanguageIdentifier) -> TokenStream {
let (lang, script, region, variants) = id.clone().into_raw_parts();
let lang = if let Some(lang) = lang {
quote!(Some(TinyStr8::new_unchecked(#lang)))
} else {
quote!(None)
};
let script = if let Some(script) = script {
quote!(Some(TinyStr4::new_unchecked(#script)))
} else {
quote!(None)
};
let region = if let Some(region) = region {
quote!(Some(TinyStr4::new_unchecked(#region)))
} else {
quote!(None)
};
let variants = if let Some(variants) = variants {
let v: Vec<_> = variants
.iter()
.map(|v| quote!(TinyStr8::new_unchecked(#v)))
.collect();
quote!(Some(Box::new([#(#v,)*])))
} else {
quote!(None)
};
quote! {
unsafe { LanguageIdentifier::from_raw_parts_unchecked(
#lang,
#script,
#region,
#variants,
) }
}
}
pub fn gen_mid(
lang: &LanguageIdentifier,
pluralrule_set: &[(PluralCategory, TokenStream)],
) -> TokenStream {
let langid = gen_langid(lang);
let mut iter = pluralrule_set.iter();
let queued = iter.next();
let rule_tokens = match queued {
Some(pair) => {
let mut tokens = create_return(pair.0, &pair.1);
for pair in iter {
let condition = create_return(pair.0, &pair.1);
tokens = quote! { #tokens else #condition };
}
tokens = quote! { #tokens else { PluralCategory::OTHER } };
tokens
}
None => quote! { { PluralCategory::OTHER } },
};
quote! {(
#langid,
|po| {
#rule_tokens
}
)}
}