Skip to main content

fluent_static_codegen/
language.rs

1use std::{
2    collections::{BTreeMap, BTreeSet},
3    rc::Rc,
4};
5
6use convert_case::{Case, Casing};
7use fluent_static_value::{Number, Value};
8use fluent_syntax::ast;
9use intl_pluralrules::PluralCategory;
10use proc_macro2::{Ident, Literal, TokenStream as TokenStream2};
11use quote::{format_ident, quote};
12use unic_langid::LanguageIdentifier;
13
14use crate::{
15    ast::{Node, Visitor},
16    function::FunctionCallGenerator,
17    types::{FluentId, FluentMessage, FluentVariable, PublicFluentId},
18    Error,
19};
20
21#[derive(Debug, Clone)]
22enum ExpressionContext {
23    Inline,
24    Selector {
25        plural_rules: bool,
26    },
27    TermArguments {
28        term: FluentMessage,
29    },
30    FunctionCall {
31        positional_args: Ident,
32        named_args: Ident,
33    },
34}
35
36pub struct LanguageBuilder {
37    pending_fns: Vec<FluentMessage>,
38    expression_contexts: Vec<ExpressionContext>,
39    fn_call_generator: Rc<dyn FunctionCallGenerator>,
40
41    #[allow(dead_code)]
42    pub language_id: LanguageIdentifier,
43    pub prefix: String,
44    pub registered_fns: BTreeMap<FluentId, FluentMessage>,
45    pub registered_message_fns: BTreeMap<PublicFluentId, FluentMessage>,
46}
47
48impl LanguageBuilder {
49    pub fn new(
50        language_id: &LanguageIdentifier,
51        fn_call_generator: Rc<dyn FunctionCallGenerator>,
52    ) -> Self {
53        Self {
54            language_id: language_id.clone(),
55            fn_call_generator,
56            prefix: language_id.to_string().to_case(Case::Snake),
57            pending_fns: Vec::new(),
58            registered_fns: BTreeMap::new(),
59            registered_message_fns: BTreeMap::new(),
60            expression_contexts: Vec::new(),
61        }
62    }
63
64    fn push_message<S: ToString>(&mut self, message: &ast::Message<S>) {
65        let id = &message.id;
66        self.pending_fns.push(FluentMessage::new(
67            id,
68            self.make_fn_ident(&message.id, None),
69            false,
70        ));
71    }
72
73    fn push_term<S: ToString>(&mut self, term: &ast::Term<S>) {
74        self.pending_fns.push(FluentMessage::new(
75            &term.id,
76            self.make_fn_ident(&term.id, None),
77            true,
78        ));
79    }
80
81    fn push_attribute<S: ToString>(&mut self, attribute: &ast::Attribute<S>) -> Result<(), Error> {
82        if let Some(parent) = self.pending_fns.last() {
83            let id = parent.id().join(&attribute.id);
84            let fn_ident = self.make_fn_ident(parent.id().clone(), Some(attribute.into()));
85            self.pending_fns
86                .push(FluentMessage::new(id, fn_ident, parent.is_private()));
87            Ok(())
88        } else {
89            Err(Error::UnexpectedContextState)
90        }
91    }
92
93    fn generate_message_code(&self, msg: &FluentMessage, body: TokenStream2) -> TokenStream2 {
94        let fn_ident = msg.fn_ident();
95        let var_idents: BTreeSet<Ident> = msg.vars().into_iter().map(|v| v.var_ident).collect();
96
97        let fn_generics = if var_idents.is_empty() {
98            quote! {
99                <W: ::std::fmt::Write>
100            }
101        } else {
102            quote! {
103                <'a, W: ::std::fmt::Write>
104            }
105        };
106
107        quote! {
108            #[inline]
109            fn #fn_ident #fn_generics(
110                &self,
111                out: &mut W,
112                #(#var_idents: ::fluent_static::value::Value<'a>),*
113            ) -> ::std::fmt::Result {
114                #body
115                Ok(())
116            }
117        }
118    }
119
120    fn register_pending_fn(&mut self, body: TokenStream2) -> Result<TokenStream2, Error> {
121        let f = self
122            .pending_fns
123            .pop()
124            .ok_or(Error::UnexpectedContextState)?;
125
126        let result = self.generate_message_code(&f, body);
127
128        if self.registered_fns.insert(f.id(), f.clone()).is_some() {
129            Err(Error::DuplicateEntryId(f.id().to_string()))
130        } else {
131            if !f.is_private() {
132                self.registered_message_fns.insert(f.public_id(), f.clone());
133            }
134            Ok(result)
135        }
136    }
137
138    fn append_var<S: ToString>(&mut self, id: &ast::Identifier<S>) -> Result<Ident, Error> {
139        if let Some(item) = self.pending_fns.last_mut() {
140            let var_name = id.name.to_string();
141            let var_ident = format_ident!("{}", var_name.to_case(Case::Snake));
142            let var = FluentVariable::new(var_name, var_ident.clone());
143            item.add_var(var);
144            Ok(var_ident)
145        } else {
146            Err(Error::UnexpectedContextState)
147        }
148    }
149
150    fn current_context(&self) -> Result<&FluentMessage, Error> {
151        self.pending_fns.last().ok_or(Error::UnexpectedContextState)
152    }
153
154    fn make_fn_ident<I: Into<FluentId>>(&self, id: I, attribute: Option<I>) -> Ident {
155        let id = id.into().as_ref().to_case(Case::Snake);
156        if let Some(attribute) = attribute {
157            let attr_id = attribute.into().as_ref().to_case(Case::Snake);
158            format_ident!("{}_{}_{}", self.prefix, id, attr_id,)
159        } else {
160            format_ident!("{}_{}", self.prefix, id)
161        }
162    }
163
164    fn enter_expr_context(&mut self, ctx: ExpressionContext) {
165        self.expression_contexts.push(ctx);
166    }
167
168    fn leave_expr_context(&mut self) -> Result<ExpressionContext, Error> {
169        self.expression_contexts
170            .pop()
171            .ok_or(Error::UnexpectedContextState)
172    }
173
174    fn current_expr_context(&self) -> &ExpressionContext {
175        self.expression_contexts
176            .last()
177            .unwrap_or(&ExpressionContext::Inline)
178    }
179
180    fn expr_context_depth(&self) -> usize {
181        self.expression_contexts.len()
182    }
183
184    fn find_entry<S: ToString>(
185        &self,
186        id: &ast::Identifier<S>,
187        attribute: Option<&ast::Identifier<S>>,
188    ) -> (FluentId, Option<FluentMessage>) {
189        let msg_id = if let Some(attribute_id) = attribute {
190            FluentId::from(id).join(attribute_id)
191        } else {
192            FluentId::from(id)
193        };
194        (msg_id.clone(), self.registered_fns.get(&msg_id).cloned())
195    }
196}
197
198impl<S: ToString> Visitor<S> for LanguageBuilder {
199    type Output = Result<TokenStream2, Error>;
200
201    fn visit_resource(&mut self, resource: &ast::Resource<S>) -> Self::Output {
202        resource
203            .body
204            .iter()
205            .try_fold(TokenStream2::new(), |mut result, entry| {
206                let tokens = entry.accept(self)?;
207                result.extend(tokens);
208                Ok(result)
209            })
210    }
211
212    fn visit_entry(&mut self, entry: &ast::Entry<S>) -> Self::Output {
213        match entry {
214            ast::Entry::Message(message) => message.accept(self),
215            ast::Entry::Term(term) => term.accept(self),
216            _ => Ok(TokenStream2::new()),
217        }
218    }
219
220    fn visit_message(&mut self, message: &ast::Message<S>) -> Self::Output {
221        self.push_message(message);
222        let body = message
223            .value
224            .as_ref()
225            .map(|pattern| pattern.accept(self))
226            .unwrap_or_else(|| Ok(TokenStream2::new()))?;
227
228        let attribute_fns = message
229            .attributes
230            .iter()
231            .map(|attribute| attribute.accept(self))
232            .collect::<Result<Vec<TokenStream2>, Error>>()?;
233
234        let message_fn = self.register_pending_fn(body)?;
235
236        Ok(quote! {
237            #message_fn
238            #(#attribute_fns)*
239        })
240    }
241
242    fn visit_term(&mut self, term: &ast::Term<S>) -> Self::Output {
243        self.push_term(term);
244        let body = term.value.accept(self)?;
245
246        let attribute_fns = term
247            .attributes
248            .iter()
249            .map(|attribute| attribute.accept(self))
250            .collect::<Result<Vec<TokenStream2>, Error>>()?;
251
252        let term_fn = self.register_pending_fn(body)?;
253
254        Ok(quote! {
255            #term_fn
256            #(#attribute_fns)*
257        })
258    }
259
260    fn visit_pattern(&mut self, pattern: &ast::Pattern<S>) -> Self::Output {
261        let elements: Vec<TokenStream2> = pattern
262            .elements
263            .iter()
264            .map(|element| element.accept(self))
265            .collect::<Result<Vec<TokenStream2>, Error>>()?;
266        Ok(quote! {
267            #(#elements)*
268        })
269    }
270
271    fn visit_text_element(&mut self, value: &S) -> Self::Output {
272        let text = Literal::string(value.to_string().as_str());
273        Ok(quote! {
274            out.write_str(#text)?;
275        })
276    }
277
278    fn visit_attribute(&mut self, attribute: &ast::Attribute<S>) -> Self::Output {
279        self.push_attribute(attribute)?;
280        let body = attribute.value.accept(self)?;
281        self.register_pending_fn(body)
282    }
283
284    fn visit_variant(
285        &mut self,
286        variant_key: &ast::VariantKey<S>,
287        pattern: &ast::Pattern<S>,
288        is_default: bool,
289    ) -> Self::Output {
290        let match_key = if is_default {
291            quote! {
292                _
293            }
294        } else {
295            match variant_key {
296                ast::VariantKey::Identifier { name } => {
297                    let name = name.to_string();
298                    let lit = Literal::string(&name);
299                    if get_plural_category(variant_key).is_some() {
300                        let category_ident = format_ident!("{}", &name.to_uppercase());
301                        quote! {
302                           (Some(::std::borrow::Cow::Borrowed(#lit)), _, _) | (_, _, Some(::fluent_static::intl_pluralrules::PluralCategory::#category_ident))
303                        }
304                    } else {
305                        quote! {
306                            (Some(::std::borrow::Cow::Borrowed(#lit)), None, None)
307                        }
308                    }
309                }
310                ast::VariantKey::NumberLiteral { value } => {
311                    let number = mk_number(value)?;
312                    quote! {
313                        (None, Some(n), _) if n == #number
314                    }
315                }
316            }
317        };
318
319        let body = pattern.accept(self)?;
320
321        Ok(quote! {
322            #match_key => {
323                #body
324            }
325        })
326    }
327
328    fn visit_comment(&mut self, _comment: &ast::Comment<S>) -> Self::Output {
329        todo!()
330    }
331
332    fn visit_call_arguments(&mut self, arguments: &ast::CallArguments<S>) -> Self::Output {
333        match self.current_expr_context() {
334            ExpressionContext::TermArguments { term } => {
335                let term = term.to_owned();
336                let vars = term.vars();
337                let vars_by_name: BTreeMap<&str, &Ident> = vars
338                    .iter()
339                    .map(|var| (var.var_name.as_str(), &var.var_ident))
340                    .collect();
341                let mut sorted_args: BTreeMap<&Ident, TokenStream2> = BTreeMap::new();
342                for named_arg in arguments.named.iter() {
343                    let name = named_arg.name.name.to_string();
344                    if let Some(ident) = vars_by_name.get(name.as_str()) {
345                        let tokens = named_arg.accept(self)?;
346                        sorted_args.insert(ident, tokens);
347                    } else {
348                        let term_id = term.id().to_string();
349                        return Err(Error::UndeclaredTermArgument {
350                            term_id,
351                            arg_name: name,
352                        });
353                    };
354                }
355                let args: Vec<TokenStream2> = sorted_args.into_values().collect();
356                Ok(quote! {
357                    #(#args),*
358                })
359            }
360            ExpressionContext::Inline => Err(Error::UnexpectedContextState),
361            ExpressionContext::Selector { .. } => Err(Error::UnexpectedContextState),
362            ExpressionContext::FunctionCall {
363                positional_args,
364                named_args,
365                ..
366            } => {
367                let positional_args_ident = positional_args.clone();
368                let named_args_ident = named_args.clone();
369
370                let positional: Vec<TokenStream2> = arguments
371                    .positional
372                    .iter()
373                    .map(|expr| expr.accept(self))
374                    .collect::<Result<Vec<TokenStream2>, Error>>()?;
375
376                let named = arguments
377                    .named
378                    .iter()
379                    .map(|arg| {
380                        arg.value.accept(self).map(|val| {
381                            let name = Literal::string(&arg.name.name.to_string());
382                            quote! {
383                                (#name, #val)
384                            }
385                        })
386                    })
387                    .collect::<Result<Vec<TokenStream2>, Error>>()?;
388                Ok(quote! {
389                    let #positional_args_ident = [ #(#positional),* ];
390                    let #named_args_ident = [ #(#named),* ];
391                })
392            }
393        }
394    }
395
396    fn visit_named_argument(&mut self, argument: &ast::NamedArgument<S>) -> Self::Output {
397        argument.value.accept(self)
398    }
399
400    fn visit_string_literal(&mut self, value: &S) -> Self::Output {
401        match self.current_expr_context() {
402            ExpressionContext::Inline => {
403                let literal = Literal::string(&value.to_string());
404                Ok(quote! {
405                    out.write_str(#literal)?;
406                })
407            }
408            ExpressionContext::Selector { .. } => Err(Error::UnsupportedFeature {
409                feature: "Usage of string literal as a selector".to_string(),
410                id: self.current_context()?.id().to_string(),
411            }),
412            ExpressionContext::TermArguments { .. } | ExpressionContext::FunctionCall { .. } => {
413                let lit = Literal::string(&value.to_string());
414                Ok(quote! {
415                    ::fluent_static::value::Value::from(#lit)
416                })
417            }
418        }
419    }
420
421    fn visit_number_literal(&mut self, value: &S) -> Self::Output {
422        match self.current_expr_context() {
423            ExpressionContext::Inline => {
424                let value = value.to_string();
425                let literal = Literal::string(&value);
426                Ok(quote! {
427                    out.write_str(#literal)?;
428                })
429            }
430            ExpressionContext::Selector { .. } => Err(Error::UnsupportedFeature {
431                feature: "Usage of number literal as a selector".to_string(),
432                id: self.current_context()?.id().to_string(),
433            }),
434            ExpressionContext::TermArguments { .. } | ExpressionContext::FunctionCall { .. } => {
435                let number = mk_number(value)?;
436                Ok(quote! {
437                    ::fluent_static::value::Value::Number { value: #number, format: None }
438                })
439            }
440        }
441    }
442
443    fn visit_function_reference(
444        &mut self,
445        id: &ast::Identifier<S>,
446        arguments: &ast::CallArguments<S>,
447    ) -> Self::Output {
448        let function_id = id.name.to_string();
449        let index = self.expr_context_depth() + 1;
450        let positional_args = format_ident!("positional_args_{}", index);
451        let named_args = format_ident!("named_args_{}", index);
452
453        let fn_call = if let Some(fn_call) =
454            self.fn_call_generator
455                .generate(&function_id, &positional_args, &named_args)
456        {
457            fn_call
458        } else {
459            return Err(Error::UnimplementedFunction {
460                entry_id: self.current_context()?.id().to_string(),
461                function_id: function_id.clone(),
462            });
463        };
464
465        self.enter_expr_context(ExpressionContext::FunctionCall {
466            positional_args,
467            named_args,
468        });
469
470        let args = arguments.accept(self)?;
471        self.leave_expr_context()?;
472
473        match self.current_expr_context() {
474            ExpressionContext::Inline => Ok(quote! {
475                {
476                    #args
477                    self._write_(&#fn_call, out)?;
478                };
479            }),
480            ExpressionContext::Selector { plural_rules } => {
481                let has_plural_rules = *plural_rules;
482                let number_expr = if has_plural_rules {
483                    quote! {
484                        {
485                            let plural_category = self.language.plural_rules_cardinal().select(n.as_f64()).ok();
486                            (None, Some(n), plural_category)
487                        }
488                    }
489                } else {
490                    quote! {
491                        (None, Some(n), None)
492                    }
493                };
494                Ok(quote! {
495                    {
496                        #args
497
498                        let fn_result = #fn_call;
499
500                        match fn_result {
501                            ::fluent_static::value::Value::String(s) => (Some(s), None, None),
502                            ::fluent_static::value::Value::Number { value: n, .. } => #number_expr,
503                            _ => (None, None, None)
504                        }
505                    }
506                })
507            }
508            ExpressionContext::TermArguments { .. } | ExpressionContext::FunctionCall { .. } => {
509                Ok(quote! {
510                    {
511                        #args
512                        #fn_call
513                    }
514                })
515            }
516        }
517    }
518
519    fn visit_message_reference(
520        &mut self,
521        id: &ast::Identifier<S>,
522        attribute: Option<&ast::Identifier<S>>,
523    ) -> Self::Output {
524        match self.current_expr_context() {
525            ExpressionContext::Inline => {
526                let (msg_id, msg) = self.find_entry(id, attribute);
527                if let Some(msg) = msg {
528                    let fn_ident = msg.fn_ident();
529                    Ok(quote! {
530                       self.#fn_ident(out)?;
531                    })
532                } else {
533                    let entry_id = self.current_context()?.id().to_string();
534                    let reference_id = msg_id.to_string();
535                    Err(Error::UndeclaredMessageReference {
536                        entry_id,
537                        reference_id,
538                    })
539                }
540            }
541            ExpressionContext::Selector { .. } => Err(Error::UnsupportedFeature {
542                feature: "Usage of message reference as selector".to_string(),
543                id: self.current_context()?.id().to_string(),
544            }),
545            ExpressionContext::TermArguments { .. } | ExpressionContext::FunctionCall { .. } => {
546                let (msg_id, msg) = self.find_entry(id, attribute);
547                if let Some(msg) = msg {
548                    let fn_ident = msg.fn_ident();
549                    Ok(quote! {
550                        {
551                            let mut out = String::new();
552                            self.#fn_ident(&mut out)?;
553                            ::fluent_static::value::Value::from(out)
554                        }
555                    })
556                } else {
557                    let entry_id = self.current_context()?.id().to_string();
558                    let reference_id = msg_id.to_string();
559                    Err(Error::UndeclaredMessageReference {
560                        entry_id,
561                        reference_id,
562                    })
563                }
564            }
565        }
566    }
567
568    fn visit_term_reference(
569        &mut self,
570        id: &ast::Identifier<S>,
571        attribute: Option<&ast::Identifier<S>>,
572        arguments: Option<&ast::CallArguments<S>>,
573    ) -> Self::Output {
574        match self.current_expr_context() {
575            ExpressionContext::Inline => {
576                let (term_id, term) = self.find_entry(id, attribute);
577                if let Some(term) = term.as_ref() {
578                    let fn_ident = term.fn_ident();
579                    let args = if let Some(args) = arguments.as_ref() {
580                        self.enter_expr_context(ExpressionContext::TermArguments {
581                            term: term.clone(),
582                        });
583                        let result = args.accept(self);
584                        self.leave_expr_context()?;
585                        result?
586                    } else {
587                        quote! {}
588                    };
589                    Ok(quote! {
590                       self.#fn_ident(out, #args)?;
591                    })
592                } else {
593                    let entry_id = self.current_context()?.id().to_string();
594                    let reference_id = term_id.to_string();
595                    Err(Error::UndeclaredTermReference {
596                        entry_id,
597                        reference_id,
598                    })
599                }
600            }
601            ExpressionContext::Selector { .. } => Err(Error::UnsupportedFeature {
602                feature: "Usage of term reference as selector".to_string(),
603                id: self.current_context()?.id().to_string(),
604            }),
605            ExpressionContext::TermArguments { .. } | ExpressionContext::FunctionCall { .. } => {
606                let (term_id, term) = self.find_entry(id, attribute);
607                if let Some(term) = term.as_ref() {
608                    let fn_ident = term.fn_ident();
609                    let args = if let Some(args) = arguments.as_ref() {
610                        self.enter_expr_context(ExpressionContext::TermArguments {
611                            term: term.clone(),
612                        });
613                        let result = args.accept(self);
614                        self.leave_expr_context()?;
615                        result?
616                    } else {
617                        quote! {}
618                    };
619                    Ok(quote! {
620                        {
621                            let mut out = String::new();
622                            self.#fn_ident(&mut out, #args)?;
623                            ::fluent_static::value::Value::from(out)
624                        }
625                    })
626                } else {
627                    let entry_id = self.current_context()?.id().to_string();
628                    let reference_id = term_id.to_string();
629                    Err(Error::UndeclaredTermReference {
630                        entry_id,
631                        reference_id,
632                    })
633                }
634            }
635        }
636    }
637
638    fn visit_variable_reference(&mut self, id: &ast::Identifier<S>) -> Self::Output {
639        match self.current_expr_context() {
640            ExpressionContext::Inline => {
641                let var_ident = self.append_var(id)?;
642                Ok(quote! {
643                    self._write_(&#var_ident, out)?;
644                })
645            }
646            ExpressionContext::Selector { plural_rules } => {
647                let has_plural_rules = *plural_rules;
648                let var_ident = self.append_var(id)?;
649                let number_expr = if has_plural_rules {
650                    quote! {
651                        {
652                            let plural_category = self.language.plural_rules_cardinal().select(n.as_f64()).ok();
653                            (None, Some(n.clone()), plural_category)
654                        }
655                    }
656                } else {
657                    quote! {
658                        (None, Some(n.clone()), None::<::fluent_static::intl_pluralrules::PluralCategory>)
659                    }
660                };
661                Ok(quote! {
662                    {
663                        match &#var_ident {
664                            ::fluent_static::value::Value::String(s) => (Some(s.clone()), None, None),
665                            ::fluent_static::value::Value::Number { value: n, .. } => #number_expr,
666                            _ => (None, None, None)
667                        }
668                    }
669                })
670            }
671            ExpressionContext::TermArguments { .. } => Err(Error::UnsupportedFeature {
672                feature: "Usage of variable reference as a term argument".to_string(),
673                id: self.current_context()?.id().to_string(),
674            }),
675            ExpressionContext::FunctionCall { .. } => {
676                let var_ident = self.append_var(id)?;
677                // TODO should value be cloned? just in case if it already used
678                Ok(quote! {
679                    #var_ident
680                })
681            }
682        }
683    }
684
685    fn visit_select_expression<'a, I>(
686        &mut self,
687        selector: &'a ast::InlineExpression<S>,
688        variants: I,
689    ) -> Self::Output
690    where
691        I: Iterator<Item = &'a ast::Variant<S>>,
692    {
693        let mut variants: Vec<&ast::Variant<S>> = variants.collect();
694        // make default variant(s) to be at the end of the list
695        variants.sort_by_key(|variant| variant.default);
696
697        let default_variants: Vec<&&ast::Variant<S>> =
698            variants.iter().filter(|variant| variant.default).collect();
699
700        // TODO this is probably redundant because parser should catch it
701        if default_variants.len() != 1 {
702            let msg_id = self.current_context()?.id().to_string();
703            Err(Error::InvalidSelectorDefaultVariant { message_id: msg_id })
704        } else {
705            // TODO ignore plural rules if there is only one category and it is default variant
706            // e.g. *[other] =
707            let plural_rules = variants
708                .iter()
709                .find(|variant| get_plural_category(&variant.key).is_some())
710                .is_some();
711
712            self.enter_expr_context(ExpressionContext::Selector { plural_rules });
713            let selector_expr = selector.accept(self)?;
714            self.leave_expr_context()?;
715            let selector_variants = variants
716                .iter()
717                .map(|variant| variant.accept(self))
718                .collect::<Result<Vec<TokenStream2>, Error>>()?;
719
720            //as (Option<&str>, Option<::fluent_static::value::Number>, Option<::fluent_static::intl_pluralrules::PluralCategory>)
721
722            Ok(quote! {
723                match #selector_expr {
724                    #(#selector_variants),*
725                }
726            })
727        }
728    }
729}
730
731fn get_plural_category<S: ToString>(key: &ast::VariantKey<S>) -> Option<PluralCategory> {
732    if let ast::VariantKey::Identifier { name } = key {
733        match name.to_string().as_str() {
734            "zero" => Some(PluralCategory::ZERO),
735            "one" => Some(PluralCategory::ONE),
736            "two" => Some(PluralCategory::TWO),
737            "few" => Some(PluralCategory::FEW),
738            "many" => Some(PluralCategory::MANY),
739            "other" => Some(PluralCategory::OTHER),
740            _ => None,
741        }
742    } else {
743        None
744    }
745}
746
747fn mk_number<S: ToString>(value: &S) -> Result<TokenStream2, Error> {
748    let value = value.to_string();
749    match Value::try_number(&value) {
750        Value::Number { value, .. } => match value {
751            Number::I64(n) => Ok(quote! {
752                ::fluent_static::value::Number::I64(#n)
753            }),
754            Number::U64(n) => Ok(quote! {
755                ::fluent_static::value::Number::U64(#n)
756            }),
757            Number::I128(n) => Ok(quote! {
758                ::fluent_static::value::Number::I128(#n)
759            }),
760            Number::U128(n) => Ok(quote! {
761                ::fluent_static::value::Number::U128(#n)
762            }),
763            Number::F64(n) => Ok(quote! {
764                ::fluent_static::value::Number::F64(#n)
765            }),
766        },
767        _ => Err(Error::InvalidLiteral(value)),
768    }
769}