Will need a refactor to support more complex use-cases - currently only vecs are supported.
XPGOKS6XWM2Q2R74DDEHQRMZH6BWQH2FMQGAXBLO7FW52J3VFKSQC XDJBTEXUZNIAC2TKC4Z3OZORWAXR4ZWYUHNM6OEGXTL6WZDXOVZQC VNSHGQYNPGKGGPYNVP4Z2RWD7JCSDJVYAADD6UXWBYL6ZRXKLE4AC XEEXWJLGVIPIGURSDU4ETZMGAIFTFDPECM4QWFOSRHU7GMGVOUVQC 2SITVDYW6KANM24QXRHVSBL6S77UHKJLOSOHSUZQBJFL5NAAGQYAC QFPQZR4K4UZ7R2GQZJG4NYBGVQJVL2ANIKGGTOHAMIRIBQHPSQGAC 3NMKD6I57ONAGHEN4PZIAV2KPYESVR4JL3DTWSHXKCMVJBEQ4GIQC 5TEX4MNUC4LDDRMNEOVCFNUUEZAGUXMKO3OIEQFXWRQKXSHY2NRQC CESJ4CTO26X4GBZBPXRXLOJT3JQJOGFN5EJSNAAZELNQRZF7QSYAC length = Item count: { LEN($items) }numbers = { LEN($items) ->[0] No items[1] One item*[other] { LEN($items) } items}plurals = { LEN($items) ->[one] One item*[other] { LEN($items) } items}
//! End-to-end test for function support in the `l10n_embed_derive` macromod common;use common::compare_message;use icu_locale::{Locale, locale};use l10n_embed_derive::localize;use rstest::rstest;const DEFAULT_LOCALE: Locale = locale!("en-US");#[localize("tests/locale/**/functions.ftl")]pub enum MessageEnum {Length { items: Vec<String> },Numbers { items: Vec<String> },Plurals { items: Vec<String> },}#[localize("tests/locale/**/functions.ftl")]pub struct Length {items: Vec<String>,}#[localize("tests/locale/**/functions.ftl")]pub struct Numbers {items: Vec<String>,}#[localize("tests/locale/**/functions.ftl")]pub struct Plurals {items: Vec<String>,}#[rstest]#[case::zero(0)]#[case::one(1)]#[case::two(2)]fn len(#[case] item_count: usize) {let items: Vec<String> = (0..item_count).map(|index| format!("Item {index}")).collect();compare_message(MessageEnum::Length {items: items.clone(),},format!("Item count: {item_count}"),DEFAULT_LOCALE,);compare_message(Length { items },format!("Item count: {item_count}"),DEFAULT_LOCALE,);}#[rstest]#[case::zero(0, "No items")]#[case::one(1, "One item")]#[case::two(2, "2 items")]fn numbers(#[case] item_count: usize, #[case] expected: &str) {let items: Vec<String> = (0..item_count).map(|index| format!("Item {index}")).collect();compare_message(MessageEnum::Numbers {items: items.clone(),},expected,DEFAULT_LOCALE,);compare_message(Numbers { items }, expected, DEFAULT_LOCALE);}#[rstest]#[case::zero(0, "0 items")]#[case::one(1, "One item")]#[case::two(2, "2 items")]fn plurals(#[case] item_count: usize, #[case] expected: &str) {let items: Vec<String> = (0..item_count).map(|index| format!("Item {index}")).collect();compare_message(MessageEnum::Plurals {items: items.clone(),},expected,DEFAULT_LOCALE,);compare_message(Plurals { items }, expected, DEFAULT_LOCALE);}
#[error("unable to parse Fluent function")]pub enum FunctionError {InvalidName {name: String,#[source_code]source_code: NamedSource<String>,// TODO: blocked on https://github.com/projectfluent/fluent-rs/pull/373// #[label("This key isn't in the `{canonical_locale}` locale")]// span: SourceSpan,},IncorrectPositionalArgumentCount {expected_len: usize,actual_len: usize,#[source_code]source_code: NamedSource<String>,// TODO: blocked on https://github.com/projectfluent/fluent-rs/pull/373// #[label("This key isn't in the `{canonical_locale}` locale")]// span: SourceSpan,},UnexpectedNamedArguments {#[source_code]source_code: NamedSource<String>,// TODO: blocked on https://github.com/projectfluent/fluent-rs/pull/373// #[label("This key isn't in the `{canonical_locale}` locale")]// span: SourceSpan,},NonVariableReference {#[source_code]source_code: NamedSource<String>,// TODO: blocked on https://github.com/projectfluent/fluent-rs/pull/373// #[label("This key isn't in the `{canonical_locale}` locale")]// span: SourceSpan,},}#[derive(Diagnostic, Debug, Error)]
// Only dereference if enum variantlet category_reference =match message_context.derive_context.reference_kind {
let category_reference = match selector {// Can't dereference value returned by functionInlineExpression::FunctionReference { .. } => {quote!(#raw_match_target)}// Only dereference if enum variant_ => match message_context.derive_context.reference_kind {
InlineExpression::FunctionReference { id, arguments } => {match id.name.to_snake_case().as_str() {"len" => {if !arguments.named.is_empty() {return Err(Error::Function(FunctionError::UnexpectedNamedArguments {source_code: message_context.source.named_source.clone(),}));}let argument = match arguments.positional.as_slice() {[single_argument] => single_argument,_ => {return Err(Error::Function(FunctionError::IncorrectPositionalArgumentCount {expected_len: 1,actual_len: arguments.positional.len(),source_code: message_context.source.named_source.clone(),},));}};let variable_reference = match argument {InlineExpression::VariableReference { .. } => {inline_expression(argument, message_context)?}_ => {return Err(Error::Function(FunctionError::NonVariableReference {source_code: message_context.source.named_source.clone(),}));}};let vec_reference = match message_context.derive_context.reference_kind {ReferenceKind::EnumField => quote!(#variable_reference),ReferenceKind::StructField => quote!(&#variable_reference),};parse_quote!(Vec::len(#vec_reference))}_ => {return Err(Error::Function(FunctionError::InvalidName {name: id.name.clone(),source_code: message_context.source.named_source.clone(),}));}}}