use std::collections::{BTreeMap, BTreeSet};
use leptos_i18n_parser::{
formatters::VarBounds,
parse_locales::{
locale::{DefaultedLocales, InterpolationKeys, Locale},
options::ParseOptions,
parsed_value::ParsedValue,
},
utils::{Key, KeyPath, UnwrapAt},
};
use proc_macro2::{Span, TokenStream};
use quote::{ToTokens, format_ident, quote};
use super::parsed_value;
use super::{parsed_value::TRANSLATIONS_KEY, ranges::RangeType, strings_accessor_method_name};
use crate::utils::EitherOfWrapper;
pub const LOCALE_FIELD_KEY: &str = "_locale";
#[derive(Clone)]
enum EitherIter<A, B> {
Iter1(A),
Iter2(B),
}
impl<T, A: Iterator<Item = T>, B: Iterator<Item = T>> Iterator for EitherIter<A, B> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
match self {
EitherIter::Iter1(iter) => iter.next(),
EitherIter::Iter2(iter) => iter.next(),
}
}
}
pub struct Interpolation {
pub ident: syn::Ident,
pub imp: TokenStream,
pub docs: TokenStream,
}
#[derive(Debug, Copy, Clone)]
enum RangeOrPlural {
Range(RangeType),
Plural,
}
impl From<leptos_i18n_parser::parse_locales::locale::RangeOrPlural> for RangeOrPlural {
fn from(value: leptos_i18n_parser::parse_locales::locale::RangeOrPlural) -> Self {
match value {
leptos_i18n_parser::parse_locales::locale::RangeOrPlural::Range(range_type) => {
RangeOrPlural::Range(range_type.into())
}
leptos_i18n_parser::parse_locales::locale::RangeOrPlural::Plural => {
RangeOrPlural::Plural
}
}
}
}
impl RangeOrPlural {
pub fn to_bound(self) -> TokenStream {
match self {
RangeOrPlural::Range(range_type) => {
quote!(l_i18n_crate::__private::InterpolateRangeCount<#range_type>)
}
RangeOrPlural::Plural => {
quote!(l_i18n_crate::__private::InterpolatePluralCount)
}
}
}
}
enum VarOrComp {
Var {
bounds: Vec<VarBounds>,
plural: Option<RangeOrPlural>,
},
Comp {
into_view: syn::Ident,
self_closed: bool,
},
}
struct Field {
key: Key,
generic: syn::Ident,
var_or_comp: VarOrComp,
}
impl Field {
fn get_var_generics(
generic: &syn::Ident,
bounds: &[VarBounds],
plural: Option<RangeOrPlural>,
) -> TokenStream {
let bounds = bounds.iter().map(VarBounds::view_bounds);
let plural_bound = plural.map(RangeOrPlural::to_bound);
let bounds = bounds.chain(plural_bound);
quote!(#generic: 'static + ::core::clone::Clone #(+ #bounds)*)
}
fn get_fmt_var_generics(
generic: &syn::Ident,
bounds: &[VarBounds],
range: Option<RangeOrPlural>,
) -> Option<TokenStream> {
match range {
None => {
let bounds = bounds.iter().map(VarBounds::fmt_bounds);
Some(quote!(#generic: #(#bounds +)*))
}
Some(RangeOrPlural::Range(_)) => None,
Some(RangeOrPlural::Plural) => {
let bounds = bounds.iter().map(VarBounds::fmt_bounds);
Some(
quote!(#generic: #(#bounds +)* Clone + Into<l_i18n_crate::reexports::icu::plurals::PluralOperands>),
)
}
}
}
pub fn as_bounded_generic(&self) -> impl Iterator<Item = TokenStream> {
let generic = &self.generic;
match &self.var_or_comp {
VarOrComp::Var { bounds, plural } => {
let ts = Self::get_var_generics(generic, bounds, *plural);
EitherIter::Iter1(std::iter::once(ts))
}
VarOrComp::Comp {
into_view,
self_closed,
} => {
let ts = [
if *self_closed {
quote!(#generic: l_i18n_crate::__private::InterpolateCompSelfClosed<#into_view>)
} else {
quote!(#generic: l_i18n_crate::__private::InterpolateComp<#into_view>)
},
quote!(#into_view: l_i18n_crate::__private::AttributesArgMarker),
];
EitherIter::Iter2(ts.into_iter())
}
}
}
pub fn as_fmt_bounded_generic(&self) -> impl Iterator<Item = TokenStream> {
let generic = &self.generic;
match &self.var_or_comp {
VarOrComp::Var { bounds, plural } => {
let ts = Self::get_fmt_var_generics(generic, bounds, *plural);
EitherIter::Iter1(ts.into_iter())
}
VarOrComp::Comp {
into_view,
self_closed: _,
} => {
let ts = [
quote!(#generic: l_i18n_crate::display::DisplayComponent<#into_view>),
quote!(#into_view),
];
EitherIter::Iter2(ts.into_iter())
}
}
}
pub fn as_right_generics(&self) -> impl Iterator<Item = &syn::Ident> {
let generic = std::iter::once(&self.generic);
match &self.var_or_comp {
VarOrComp::Var { .. } => EitherIter::Iter1(generic),
VarOrComp::Comp { into_view, .. } => EitherIter::Iter2(generic.chain(Some(into_view))),
}
}
pub fn as_string_right_generics(&self) -> impl Iterator<Item = TokenStream> {
let generic = std::iter::once(self.generic.to_token_stream());
match &self.var_or_comp {
VarOrComp::Var {
plural: Some(RangeOrPlural::Range(range_type)),
..
} => EitherIter::Iter1(std::iter::once(quote!(#range_type))),
VarOrComp::Var { .. } => EitherIter::Iter1(generic),
VarOrComp::Comp { into_view, .. } => {
EitherIter::Iter2(generic.chain(Some(quote!(#into_view))))
}
}
}
pub fn as_string_builder_marker(&self) -> TokenStream {
match &self.var_or_comp {
VarOrComp::Var {
plural: Some(RangeOrPlural::Range(range_type)),
..
} => quote!(#range_type),
_ => self.generic.to_token_stream(),
}
}
pub fn as_struct_field(&self) -> TokenStream {
let Field { key, generic, .. } = self;
quote!(#key: #generic)
}
pub fn as_into_view_generic(&self) -> Option<&syn::Ident> {
match &self.var_or_comp {
VarOrComp::Var { .. } => None,
VarOrComp::Comp { into_view, .. } => Some(into_view),
}
}
}
impl Interpolation {
fn make_fields(keys: &InterpolationKeys) -> Vec<Field> {
let vars = keys.iter_vars().map(|(key, infos)| {
let mut bounds = infos.bounds.iter().cloned().collect::<Vec<_>>();
bounds.sort(); let var_or_comp = VarOrComp::Var {
bounds,
plural: infos.range_count.map(Into::into),
};
let generic = format_ident!("__{}__", key);
Field {
key,
var_or_comp,
generic,
}
});
let comps = keys.iter_comps().map(|key| {
let into_view = format_ident!("__into_view_{}__", key);
let var_or_comp = VarOrComp::Comp {
into_view,
self_closed: false,
};
let generic = format_ident!("__{}__", key);
Field {
key,
var_or_comp,
generic,
}
});
let comps_self_closed = keys.iter_comps_self_closed().map(|key| {
let into_view = format_ident!("__into_view_{}__", key);
let var_or_comp = VarOrComp::Comp {
into_view,
self_closed: true,
};
let generic = format_ident!("__{}__", key);
Field {
key,
var_or_comp,
generic,
}
});
let mut fields: Vec<_> = vars.chain(comps).chain(comps_self_closed).collect();
fields.sort_by(|a, b| a.key.cmp(&b.key));
fields
}
pub fn new(
key: &Key,
enum_ident: &syn::Ident,
keys: &InterpolationKeys,
locales: &[Locale],
key_path: &KeyPath,
locale_type_ident: &syn::Ident,
defaults: &DefaultedLocales,
options: &ParseOptions,
gen_docs: bool,
) -> Self {
let locales = locales
.iter()
.filter(|locale| {
locale
.keys
.get(key)
.is_some_and(|v| !matches!(v, ParsedValue::Default))
})
.collect::<Vec<_>>();
let builder_name = format!("{key}_builder");
let ident = syn::Ident::new(&builder_name, Span::call_site());
let dummy_ident = format_ident!("{}_dummy", ident);
let locale_field = Key::new(LOCALE_FIELD_KEY).unwrap_at("LOCALE_FIELD_KEY");
let into_view_field = Key::new("_into_views_marker").unwrap_at("Interpolation::new_1");
let typed_builder_name = format_ident!("{}Builder", ident);
let display_struct_ident = format_ident!("{}Display", ident);
let computed_defaults = defaults.compute();
let fields = Self::make_fields(keys);
let type_def = Self::create_types(
&ident,
&dummy_ident,
enum_ident,
&locale_field,
&into_view_field,
&fields,
);
let dummy_impl = Self::dummy_impl(
&ident,
&dummy_ident,
enum_ident,
&typed_builder_name,
&locale_field,
&into_view_field,
&fields,
options,
);
let into_view_impl = Self::into_view_impl(
key,
&ident,
enum_ident,
&locale_field,
&fields,
&locales,
key_path,
locale_type_ident,
&computed_defaults,
options,
);
let debug_impl = Self::debug_impl(&builder_name, &ident, &fields);
let (display_impl, builder_display) = if options.interpolate_display {
let display_impl = Self::display_impl(
key,
&ident,
&display_struct_ident,
enum_ident,
&locale_field,
&fields,
&locales,
locale_type_ident,
&computed_defaults,
);
let builder_display = Self::builder_string_build_fns(
enum_ident,
&typed_builder_name,
&display_struct_ident,
&fields,
);
(display_impl, builder_display)
} else {
(quote!(), quote!())
};
let imp = quote! {
#type_def
#into_view_impl
#debug_impl
#display_impl
#builder_display
#dummy_impl
};
let docs = if gen_docs {
let path = key_path.to_string_with_key(key);
let mut docs = format!("Full path: `{}`\n", path);
Self::gen_fields_docs(&mut docs, &fields).unwrap();
quote! {
#[doc = #docs]
}
} else {
quote! {}
};
Self {
imp,
ident: dummy_ident,
docs,
}
}
fn gen_fields_docs(docs: &mut String, fields: &[Field]) -> core::fmt::Result {
use core::fmt::Write;
let mut variables = fields
.iter()
.filter_map(|field| match &field.var_or_comp {
VarOrComp::Var { bounds, plural } => {
let key = field.key.name.strip_prefix("var_")?;
Some((key, bounds.as_slice(), plural.as_ref()))
}
VarOrComp::Comp { .. } => None,
})
.peekable();
if variables.peek().is_some() {
writeln!(docs, "## Vars :")?;
for (key, bounds, plural) in variables {
let _ = bounds;
match plural {
Some(_) => writeln!(docs, "- `{}` (plural count)", key)?,
None => writeln!(docs, "- `{}`", key)?,
}
}
}
let mut components = fields
.iter()
.filter_map(|field| match &field.var_or_comp {
VarOrComp::Var { .. } => None,
VarOrComp::Comp { self_closed, .. } => {
let key = field.key.name.strip_prefix("comp_")?;
Some((key, *self_closed))
}
})
.peekable();
if components.peek().is_some() {
writeln!(docs, "## Components :")?;
for (key, self_closed) in components {
if self_closed {
writeln!(docs, "- `<{}/>`", key)?;
} else {
writeln!(docs, "- `<{}>`", key)?;
}
}
}
Ok(())
}
fn builder_string_build_fns(
enum_ident: &syn::Ident,
typed_builder_name: &syn::Ident,
display_struct_ident: &syn::Ident,
fields: &[Field],
) -> TokenStream {
let left_generics = fields.iter().flat_map(Field::as_fmt_bounded_generic);
let right_generics = fields.iter().flat_map(Field::as_string_right_generics);
let marker = fields.iter().map(Field::as_string_builder_marker);
let into_views = fields.iter().filter_map(Field::as_into_view_generic);
let fns = if cfg!(all(feature = "dynamic_load", not(feature = "ssr"))) {
quote! {
#[inline]
pub async fn build_display(self) -> impl std::fmt::Display {
let inner = self.build();
#display_struct_ident::new(inner).await
}
#[inline]
pub async fn build_string(self) -> String {
self.build_display().await.to_string()
}
}
} else if cfg!(all(feature = "dynamic_load", feature = "ssr")) {
quote! {
#[inline]
pub async fn build_display(self) -> impl std::fmt::Display {
let inner = self.build();
#display_struct_ident::new(inner)
}
#[inline]
pub async fn build_string(self) -> String {
self.build_display().await.to_string()
}
}
} else {
quote! {
#[inline]
pub fn build_display(self) -> impl std::fmt::Display {
let inner = self.build();
#display_struct_ident::new(inner)
}
#[inline]
pub fn build_string(self) -> String {
self.build_display().to_string()
}
}
};
quote! {
#[allow(non_camel_case_types)]
impl<#(#left_generics,)*> #typed_builder_name<#(#right_generics,)* ((#enum_ident,), (core::marker::PhantomData<(#(#into_views,)*)>,), #((#marker,),)*)> {
#fns
}
}
}
fn display_builder_fn(
ident: &syn::Ident,
enum_ident: &syn::Ident,
typed_builder_name: &syn::Ident,
locale_field: &Key,
into_view_field: &Key,
fields: &[Field],
) -> TokenStream {
let left_generics = fields.iter().flat_map(Field::as_fmt_bounded_generic);
let right_generics = fields.iter().flat_map(Field::as_string_right_generics);
let builder_marker = fields.iter().map(|_| quote!(()));
let into_views = fields.iter().filter_map(Field::as_into_view_generic);
quote! {
#[allow(non_camel_case_types)]
pub fn display_builder<#(#left_generics,)*>(self) -> #typed_builder_name<#(#right_generics,)* ((#enum_ident,), (core::marker::PhantomData<(#(#into_views,)*)>,), #(#builder_marker,)*)> {
#ident::builder().#locale_field(self.#locale_field).#into_view_field(core::marker::PhantomData)
}
}
}
fn dummy_impl(
ident: &syn::Ident,
dummy_ident: &syn::Ident,
enum_ident: &syn::Ident,
typed_builder_name: &syn::Ident,
locale_field: &Key,
into_view_field: &Key,
fields: &[Field],
options: &ParseOptions,
) -> TokenStream {
let left_generics = fields.iter().flat_map(Field::as_bounded_generic);
let right_generics = fields.iter().flat_map(Field::as_right_generics);
let empty_builder_marker = fields.iter().map(|_| quote!(()));
let display_builder_fn = if options.interpolate_display {
Self::display_builder_fn(
ident,
enum_ident,
typed_builder_name,
locale_field,
into_view_field,
fields,
)
} else {
quote!()
};
let into_views = fields.iter().filter_map(Field::as_into_view_generic);
let string_builder_trait_impl = if options.interpolate_display {
quote! {
impl l_i18n_crate::__private::InterpolationStringBuilder for #dummy_ident {}
}
} else {
quote!()
};
quote! {
impl #dummy_ident {
pub const fn new(#locale_field: #enum_ident) -> Self {
Self {
#locale_field
}
}
#[allow(non_camel_case_types)]
pub fn builder<#(#left_generics,)*>(self) -> #typed_builder_name<#(#right_generics,)* ((#enum_ident,), (core::marker::PhantomData<(#(#into_views,)*)>,), #(#empty_builder_marker,)*)> {
#ident::builder().#locale_field(self.#locale_field).#into_view_field(core::marker::PhantomData)
}
#display_builder_fn
}
#string_builder_trait_impl
}
}
fn create_types(
ident: &syn::Ident,
dummy_ident: &syn::Ident,
enum_ident: &syn::Ident,
locale_field: &Key,
into_view_field: &Key,
fields: &[Field],
) -> TokenStream {
let generics = fields.iter().flat_map(Field::as_right_generics);
let into_views = fields.iter().filter_map(Field::as_into_view_generic);
let fields = fields.iter().map(Field::as_struct_field);
let into_views_marker = quote! {
#into_view_field: core::marker::PhantomData<(#(#into_views,)*)>
};
quote! {
#[allow(non_camel_case_types, non_snake_case)]
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct #dummy_ident {
#locale_field: #enum_ident
}
#[allow(non_camel_case_types, non_snake_case)]
#[derive(l_i18n_crate::reexports::typed_builder::TypedBuilder)]
#[builder(crate_module_path = l_i18n_crate::reexports::typed_builder)]
pub struct #ident<#(
#[allow(non_camel_case_types)]
#generics,
)*> {
#locale_field: #enum_ident,
#into_views_marker,
#(#fields,)*
}
}
}
fn debug_impl(builder_name: &str, ident: &syn::Ident, fields: &[Field]) -> TokenStream {
let left_generics = fields.iter().flat_map(Field::as_right_generics);
let right_generics = fields.iter().flat_map(Field::as_right_generics);
quote! {
#[allow(non_camel_case_types)]
impl<#(#left_generics,)*> core::fmt::Debug for #ident<#(#right_generics,)*> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct(#builder_name).finish()
}
}
}
}
fn display_impl(
key: &Key,
ident: &syn::Ident,
display_struct_ident: &syn::Ident,
enum_ident: &syn::Ident,
locale_field: &Key,
fields: &[Field],
locales: &[&Locale],
locale_type_ident: &syn::Ident,
defaults: &BTreeMap<Key, BTreeSet<Key>>,
) -> TokenStream {
let left_generics = fields.iter().flat_map(Field::as_fmt_bounded_generic);
let right_generics = fields.iter().flat_map(Field::as_string_right_generics);
let raw_generics = fields
.iter()
.flat_map(Field::as_right_generics)
.collect::<Vec<_>>();
let fields_key = fields.iter().map(|f| &f.key);
let destructure = quote!(let #ident { #(#fields_key,)* #locale_field, .. } = &self.1;);
let translations_holder_enum_ident = format_ident!("{}Enum", display_struct_ident);
let locales_impls = Self::create_locale_string_impl(
key,
&translations_holder_enum_ident,
locales,
locale_type_ident,
defaults,
);
let str_name = display_struct_ident.to_string();
let translations_holder_enum = if cfg!(all(feature = "dynamic_load", not(feature = "ssr")))
{
let translations_holder_enum_ident_variants = locales.iter().map(|locale| {
let top_locale = &locale.top_locale_name.ident;
let strings_count = locale.top_locale_string_count;
quote! {
#top_locale(&'static [Box<str>; #strings_count])
}
});
quote! {
#[derive(Clone, Copy)]
#[allow(non_camel_case_types, non_snake_case)]
enum #translations_holder_enum_ident {
#(
#translations_holder_enum_ident_variants,
)*
}
}
} else {
quote! {
#[allow(non_camel_case_types, non_snake_case)]
type #translations_holder_enum_ident = #enum_ident;
}
};
let new_fn = if cfg!(all(feature = "dynamic_load", not(feature = "ssr"))) {
let match_arms = locales.iter().map(|locale| {
let defaulted = defaults.get(&locale.top_locale_name).map(|defaulted_locales| {
defaulted_locales.iter().map(|key| {
quote!(| #enum_ident::#key)
}).collect::<TokenStream>()
});
let top_locale = &locale.top_locale_name.ident;
let string_accessor = strings_accessor_method_name(locale);
let strings_count = locale.top_locale_string_count;
quote! {
#enum_ident::#top_locale #defaulted => {
let translations: &'static [Box<str>; #strings_count] = super::#locale_type_ident::#string_accessor().await;
#translations_holder_enum_ident::#top_locale(translations)
}
}
});
quote! {
pub async fn new(builder: #ident<#(#raw_generics,)*>) -> Self {
let translations = match builder.#locale_field {
#(
#match_arms,
)*
};
#display_struct_ident(translations, builder)
}
}
} else {
quote! {
pub fn new(builder: #ident<#(#raw_generics,)*>) -> Self {
#display_struct_ident(builder.#locale_field, builder)
}
}
};
quote! {
#translations_holder_enum
#[allow(non_camel_case_types, non_snake_case)]
struct #display_struct_ident<#(#raw_generics,)*>(#translations_holder_enum_ident, #ident<#(#raw_generics,)*>);
#[allow(non_camel_case_types)]
impl<#(#raw_generics,)*> core::fmt::Debug for #display_struct_ident<#(#raw_generics,)*> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct(#str_name).finish()
}
}
#[allow(non_camel_case_types)]
impl<#(#left_generics,)*> ::core::fmt::Display for #display_struct_ident<#(#right_generics,)*> {
fn fmt(&self, __formatter: &mut ::core::fmt::Formatter<'_>) -> core::fmt::Result {
#destructure
match self.0 {
#(
#locales_impls,
)*
}
}
}
#[allow(non_camel_case_types)]
impl<#(#raw_generics,)*> #display_struct_ident<#(#raw_generics,)*> {
#new_fn
}
}
}
fn into_view_impl(
key: &Key,
ident: &syn::Ident,
enum_ident: &syn::Ident,
locale_field: &Key,
fields: &[Field],
locales: &[&Locale],
key_path: &KeyPath,
locale_type_ident: &syn::Ident,
defaults: &BTreeMap<Key, BTreeSet<Key>>,
options: &ParseOptions,
) -> TokenStream {
let left_generics = fields.iter().flat_map(Field::as_bounded_generic);
let right_generics = fields.iter().flat_map(Field::as_right_generics);
if options.show_keys_only {
let key = key_path.to_string_with_key(key);
return quote! {
#[allow(non_camel_case_types)]
impl<#(#left_generics,)*> #ident<#(#right_generics,)*> {
pub fn into_view(self) -> impl l_i18n_crate::reexports::leptos::IntoView + Clone + 'static {
let _ = self;
#key
}
}
};
}
let fields_key = fields.iter().map(|f| &f.key);
let destructure = quote!(let Self { #(#fields_key,)* #locale_field, .. } = self;);
let locales_impls =
Self::create_locale_impl(key, enum_ident, locales, locale_type_ident, defaults);
if cfg!(all(feature = "dynamic_load", not(feature = "ssr"))) {
quote! {
#[allow(non_camel_case_types)]
impl<#(#left_generics,)*> #ident<#(#right_generics,)*> {
pub async fn into_view(self) -> impl l_i18n_crate::reexports::leptos::IntoView + Clone + 'static {
#destructure
match #locale_field {
#(
#locales_impls,
)*
}
}
}
}
} else {
quote! {
#[allow(non_camel_case_types)]
impl<#(#left_generics,)*> #ident<#(#right_generics,)*> {
pub fn into_view(self) -> impl l_i18n_crate::reexports::leptos::IntoView + Clone + 'static {
#destructure
match #locale_field {
#(
#locales_impls,
)*
}
}
}
}
}
}
fn create_locale_impl<'a>(
key: &'a Key,
enum_ident: &'a syn::Ident,
locales: &'a [&Locale],
locale_type_ident: &'a syn::Ident,
defaults: &'a BTreeMap<Key, BTreeSet<Key>>,
) -> impl Iterator<Item = TokenStream> + 'a {
let either_wrapper = EitherOfWrapper::new(locales.len());
locales
.iter()
.enumerate()
.rev()
.map(move |(i, locale)| {
let locale_key = &locale.top_locale_name;
let value = locale
.keys
.get(key)
.unwrap_at("create_locale_impl_1");
let value = parsed_value::to_token_stream(value, locale.top_locale_string_count);
let wrapped_value = either_wrapper.wrap(i, value);
let translations_key = Key::new(TRANSLATIONS_KEY).unwrap_at("TRANSLATIONS_KEY");
let string_accessor = strings_accessor_method_name(locale);
let strings_count = locale.top_locale_string_count;
let defaulted = defaults.get(&locale.top_locale_name).map(|defaulted_locales| {
defaulted_locales.iter().map(|key| {
quote!(| #enum_ident::#key)
}).collect::<TokenStream>()
});
if cfg!(all(feature = "dynamic_load", not(feature = "ssr"))) {
quote!{
#enum_ident::#locale_key #defaulted => {
#[allow(unused)]
let #translations_key: &'static [Box<str>; #strings_count] = super::#locale_type_ident::#string_accessor().await;
#wrapped_value
}
}
} else if cfg!(all(feature = "dynamic_load", feature = "ssr")) {
quote!{
#enum_ident::#locale_key #defaulted => {
#[allow(unused)]
let #translations_key: &'static [&'static str; #strings_count] = super::#locale_type_ident::#string_accessor();
#wrapped_value
}
}
} else {
quote!{
#enum_ident::#locale_key #defaulted => {
#[allow(unused)]
const #translations_key: &[&str; #strings_count] = super::#locale_type_ident::#string_accessor();
#wrapped_value
}
}
}
})
}
fn create_locale_string_impl<'a>(
key: &'a Key,
enum_ident: &'a syn::Ident,
locales: &'a [&Locale],
locale_type_ident: &'a syn::Ident,
defaults: &'a BTreeMap<Key, BTreeSet<Key>>,
) -> impl Iterator<Item = TokenStream> + 'a {
locales.iter().rev().map(move |locale| {
let locale_key = &locale.top_locale_name;
let value = locale
.keys
.get(key)
.unwrap_at("create_locale_string_impl_1");
let value = parsed_value::as_string_impl(value, locale.top_locale_string_count);
let translations_key = Key::new(TRANSLATIONS_KEY).unwrap_at("TRANSLATIONS_KEY");
let string_accessor = strings_accessor_method_name(locale);
let strings_count = locale.top_locale_string_count;
let defaulted = defaults.get(&locale.top_locale_name).map(|defaulted_locales| {
defaulted_locales.iter().map(|key| {
quote!(| #enum_ident::#key)
}).collect::<TokenStream>()
});
if cfg!(all(feature = "dynamic_load", not(feature = "ssr"))) {
quote!{
#[allow(unused)]
#enum_ident::#locale_key(#translations_key) => {
#value
}
}
} else if cfg!(all(feature = "dynamic_load", feature = "ssr")) {
quote!{
#enum_ident::#locale_key #defaulted => {
#[allow(unused)]
let #translations_key: &[&str; #strings_count] = super::#locale_type_ident::#string_accessor();
#value
}
}
}else {
quote!{
#enum_ident::#locale_key #defaulted => {
#[allow(unused)]
const #translations_key: &[&str; #strings_count] = super::#locale_type_ident::#string_accessor();
#value
}
}
}
})
}
}