fluent_syntax/ast/
mod.rs

1//! Abstract Syntax Tree representation of the Fluent Translation List.
2//!
3//! The AST of Fluent contains all nodes structures to represent a complete
4//! representation of the FTL resource.
5//!
6//! The tree preserves all semantic information and allow for round-trip
7//! of a canonically written FTL resource.
8//!
9//! The root node is called [`Resource`] and contains a list of [`Entry`] nodes
10//! representing all possible entries in the Fluent Translation List.
11//!
12//! # Example
13//!
14//! ```
15//! use fluent_syntax::parser;
16//! use fluent_syntax::ast;
17//!
18//! let ftl = r#"
19//!
20//! ## This is a message comment
21//! hello-world = Hello World!
22//!     .tooltip = Tooltip for you, { $userName }.
23//!
24//! "#;
25//!
26//! let resource = parser::parse(ftl)
27//!     .expect("Failed to parse an FTL resource.");
28//!
29//! assert_eq!(
30//!     resource.body[0],
31//!     ast::Entry::Message(
32//!         ast::Message {
33//!             id: ast::Identifier {
34//!                 name: "hello-world"
35//!             },
36//!             value: Some(ast::Pattern {
37//!                 elements: vec![
38//!                     ast::PatternElement::TextElement {
39//!                         value: "Hello World!"
40//!                     },
41//!                 ]
42//!             }),
43//!             attributes: vec![
44//!                 ast::Attribute {
45//!                     id: ast::Identifier {
46//!                         name: "tooltip"
47//!                     },
48//!                     value: ast::Pattern {
49//!                         elements: vec![
50//!                             ast::PatternElement::TextElement {
51//!                                 value: "Tooltip for you, "
52//!                             },
53//!                             ast::PatternElement::Placeable {
54//!                                 expression: ast::Expression::Inline(
55//!                                     ast::InlineExpression::VariableReference {
56//!                                         id: ast::Identifier {
57//!                                             name: "userName"
58//!                                         }
59//!                                     }
60//!                                 )
61//!                             },
62//!                             ast::PatternElement::TextElement {
63//!                                 value: "."
64//!                             },
65//!                         ]
66//!                     }
67//!                 }
68//!             ],
69//!             comment: Some(
70//!                 ast::Comment {
71//!                     content: vec!["This is a message comment"]
72//!                 }
73//!             )
74//!         }
75//!     ),
76//! );
77//! ```
78//!
79//! ## Errors
80//!
81//! Fluent AST preserves blocks containing invalid syntax as [`Entry::Junk`].
82//!
83//! ## White space
84//!
85//! At the moment, AST does not preserve white space. In result only a
86//! canonical form of the AST is suitable for a round-trip.
87mod helper;
88
89#[cfg(feature = "serde")]
90use serde::{Deserialize, Serialize};
91
92/// Root node of a Fluent Translation List.
93///
94/// A [`Resource`] contains a body with a list of [`Entry`] nodes.
95///
96/// # Example
97///
98/// ```
99/// use fluent_syntax::parser;
100/// use fluent_syntax::ast;
101///
102/// let ftl = "";
103///
104/// let resource = parser::parse(ftl)
105///     .expect("Failed to parse an FTL resource.");
106///
107/// assert_eq!(
108///     resource,
109///     ast::Resource {
110///         body: vec![]
111///     }
112/// );
113/// ```
114#[derive(Debug, PartialEq, Clone)]
115#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
116pub struct Resource<S> {
117    pub body: Vec<Entry<S>>,
118}
119
120/// A top-level node representing an entry of a [`Resource`].
121///
122/// Every [`Entry`] is a standalone element and the parser is capable
123/// of recovering from errors by identifying a beginning of a next entry.
124///
125/// # Example
126///
127/// ```
128/// use fluent_syntax::parser;
129/// use fluent_syntax::ast;
130///
131/// let ftl = r#"
132///
133/// key = Value
134///
135/// "#;
136///
137/// let resource = parser::parse(ftl)
138///     .expect("Failed to parse an FTL resource.");
139///
140/// assert_eq!(
141///     resource,
142///     ast::Resource {
143///         body: vec![
144///             ast::Entry::Message(
145///                 ast::Message {
146///                     id: ast::Identifier {
147///                         name: "key"
148///                     },
149///                     value: Some(ast::Pattern {
150///                         elements: vec![
151///                             ast::PatternElement::TextElement {
152///                                 value: "Value"
153///                             },
154///                         ]
155///                     }),
156///                     attributes: vec![],
157///                     comment: None,
158///                 }
159///             )
160///         ]
161///     }
162/// );
163/// ```
164///
165/// # Junk Entry
166///
167/// If FTL source contains invalid FTL content, it will be preserved
168/// in form of [`Entry::Junk`] nodes.
169///
170/// # Example
171///
172/// ```
173/// use fluent_syntax::parser;
174/// use fluent_syntax::ast;
175///
176/// let ftl = r#"
177///
178/// g@rb@ge En!ry
179///
180/// "#;
181///
182/// let (resource, _) = parser::parse(ftl)
183///     .expect_err("Failed to parse an FTL resource.");
184///
185/// assert_eq!(
186///     resource,
187///     ast::Resource {
188///         body: vec![
189///             ast::Entry::Junk {
190///                 content: "g@rb@ge En!ry\n\n"
191///             }
192///         ]
193///     }
194/// );
195/// ```
196#[derive(Debug, PartialEq, Clone)]
197#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
198#[cfg_attr(feature = "serde", serde(tag = "type"))]
199pub enum Entry<S> {
200    Message(Message<S>),
201    Term(Term<S>),
202    Comment(Comment<S>),
203    GroupComment(Comment<S>),
204    ResourceComment(Comment<S>),
205    Junk { content: S },
206}
207
208/// Message node represents the most common [`Entry`] in an FTL [`Resource`].
209///
210/// A message is a localization unit with a [`Identifier`] unique within a given
211/// [`Resource`], and a value or attributes with associated [`Pattern`].
212///
213/// A message can contain a simple text value, or a compound combination of value
214/// and attributes which together can be used to localize a complex User Interface
215/// element.
216///
217/// Finally, each [`Message`] may have an associated [`Comment`].
218///
219/// # Example
220///
221/// ```
222/// use fluent_syntax::parser;
223/// use fluent_syntax::ast;
224///
225/// let ftl = r#"
226///
227/// hello-world = Hello, World!
228///
229/// "#;
230///
231/// let resource = parser::parse(ftl)
232///     .expect("Failed to parse an FTL resource.");
233///
234/// assert_eq!(
235///     resource,
236///     ast::Resource {
237///         body: vec![
238///             ast::Entry::Message(ast::Message {
239///                 id: ast::Identifier {
240///                     name: "hello-world"
241///                 },
242///                 value: Some(ast::Pattern {
243///                     elements: vec![
244///                         ast::PatternElement::TextElement {
245///                             value: "Hello, World!"
246///                         }
247///                     ]
248///                 }),
249///                 attributes: vec![],
250///                 comment: None,
251///             })
252///         ]
253///     }
254/// );
255/// ```
256#[derive(Debug, PartialEq, Clone)]
257#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
258pub struct Message<S> {
259    pub id: Identifier<S>,
260    pub value: Option<Pattern<S>>,
261    pub attributes: Vec<Attribute<S>>,
262    pub comment: Option<Comment<S>>,
263}
264
265/// A Fluent [`Term`].
266///
267/// Terms are semantically similar to [`Message`] nodes, but
268/// they represent a separate concept in Fluent system.
269///
270/// Every term has to have a value, and the parser will
271/// report errors when term references are used in wrong positions.
272///
273/// # Example
274///
275/// ```
276/// use fluent_syntax::parser;
277/// use fluent_syntax::ast;
278///
279/// let ftl = r#"
280///
281/// -brand-name = Nightly
282///
283/// "#;
284///
285/// let resource = parser::parse(ftl)
286///     .expect("Failed to parse an FTL resource.");
287///
288/// assert_eq!(
289///     resource,
290///     ast::Resource {
291///         body: vec![
292///             ast::Entry::Term(ast::Term {
293///                 id: ast::Identifier {
294///                     name: "brand-name"
295///                 },
296///                 value: ast::Pattern {
297///                     elements: vec![
298///                         ast::PatternElement::TextElement {
299///                             value: "Nightly"
300///                         }
301///                     ]
302///                 },
303///                 attributes: vec![],
304///                 comment: None,
305///             })
306///         ]
307///     }
308/// );
309/// ```
310#[derive(Debug, PartialEq, Clone)]
311#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
312pub struct Term<S> {
313    pub id: Identifier<S>,
314    pub value: Pattern<S>,
315    pub attributes: Vec<Attribute<S>>,
316    pub comment: Option<Comment<S>>,
317}
318
319/// Pattern contains a value of a [`Message`], [`Term`] or an [`Attribute`].
320///
321/// Each pattern is a list of [`PatternElement`] nodes representing
322/// either a simple textual value, or a combination of text literals
323/// and placeholder [`Expression`] nodes.
324///
325/// # Example
326///
327/// ```
328/// use fluent_syntax::parser;
329/// use fluent_syntax::ast;
330///
331/// let ftl = r#"
332///
333/// hello-world = Hello, World!
334///
335/// welcome = Welcome, { $userName }.
336///
337/// "#;
338///
339/// let resource = parser::parse(ftl)
340///     .expect("Failed to parse an FTL resource.");
341///
342/// assert_eq!(
343///     resource,
344///     ast::Resource {
345///         body: vec![
346///             ast::Entry::Message(ast::Message {
347///                 id: ast::Identifier {
348///                     name: "hello-world"
349///                 },
350///                 value: Some(ast::Pattern {
351///                     elements: vec![
352///                         ast::PatternElement::TextElement {
353///                             value: "Hello, World!"
354///                         }
355///                     ]
356///                 }),
357///                 attributes: vec![],
358///                 comment: None,
359///             }),
360///             ast::Entry::Message(ast::Message {
361///                 id: ast::Identifier {
362///                     name: "welcome"
363///                 },
364///                 value: Some(ast::Pattern {
365///                     elements: vec![
366///                         ast::PatternElement::TextElement {
367///                             value: "Welcome, "
368///                         },
369///                         ast::PatternElement::Placeable {
370///                             expression: ast::Expression::Inline(
371///                                 ast::InlineExpression::VariableReference {
372///                                     id: ast::Identifier {
373///                                         name: "userName"
374///                                     }
375///                                 }
376///                             )
377///                         },
378///                         ast::PatternElement::TextElement {
379///                             value: "."
380///                         }
381///                     ]
382///                 }),
383///                 attributes: vec![],
384///                 comment: None,
385///             }),
386///         ]
387///     }
388/// );
389/// ```
390#[derive(Debug, PartialEq, Clone)]
391#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
392pub struct Pattern<S> {
393    pub elements: Vec<PatternElement<S>>,
394}
395
396/// PatternElement is an element of a [`Pattern`].
397///
398/// Each [`PatternElement`] node represents
399/// either a simple textual value, or a combination of text literals
400/// and placeholder [`Expression`] nodes.
401///
402/// # Example
403///
404/// ```
405/// use fluent_syntax::parser;
406/// use fluent_syntax::ast;
407///
408/// let ftl = r#"
409///
410/// hello-world = Hello, World!
411///
412/// welcome = Welcome, { $userName }.
413///
414/// "#;
415///
416/// let resource = parser::parse(ftl)
417///     .expect("Failed to parse an FTL resource.");
418///
419/// assert_eq!(
420///     resource,
421///     ast::Resource {
422///         body: vec![
423///             ast::Entry::Message(ast::Message {
424///                 id: ast::Identifier {
425///                     name: "hello-world"
426///                 },
427///                 value: Some(ast::Pattern {
428///                     elements: vec![
429///                         ast::PatternElement::TextElement {
430///                             value: "Hello, World!"
431///                         }
432///                     ]
433///                 }),
434///                 attributes: vec![],
435///                 comment: None,
436///             }),
437///             ast::Entry::Message(ast::Message {
438///                 id: ast::Identifier {
439///                     name: "welcome"
440///                 },
441///                 value: Some(ast::Pattern {
442///                     elements: vec![
443///                         ast::PatternElement::TextElement {
444///                             value: "Welcome, "
445///                         },
446///                         ast::PatternElement::Placeable {
447///                             expression: ast::Expression::Inline(
448///                                 ast::InlineExpression::VariableReference {
449///                                     id: ast::Identifier {
450///                                         name: "userName"
451///                                     }
452///                                 }
453///                             )
454///                         },
455///                         ast::PatternElement::TextElement {
456///                             value: "."
457///                         }
458///                     ]
459///                 }),
460///                 attributes: vec![],
461///                 comment: None,
462///             }),
463///         ]
464///     }
465/// );
466/// ```
467#[derive(Debug, PartialEq, Clone)]
468#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
469#[cfg_attr(feature = "serde", serde(tag = "type"))]
470pub enum PatternElement<S> {
471    TextElement { value: S },
472    Placeable { expression: Expression<S> },
473}
474
475/// Attribute represents a part of a [`Message`] or [`Term`].
476///
477/// Attributes are used to express a compound list of keyed
478/// [`Pattern`] elements on an entry.
479///
480/// # Example
481///
482/// ```
483/// use fluent_syntax::parser;
484/// use fluent_syntax::ast;
485///
486/// let ftl = r#"
487///
488/// hello-world =
489///     .title = This is a title
490///     .accesskey = T
491///
492/// "#;
493///
494/// let resource = parser::parse(ftl)
495///     .expect("Failed to parse an FTL resource.");
496///
497/// assert_eq!(
498///     resource,
499///     ast::Resource {
500///         body: vec![
501///             ast::Entry::Message(ast::Message {
502///                 id: ast::Identifier {
503///                     name: "hello-world"
504///                 },
505///                 value: None,
506///                 attributes: vec![
507///                     ast::Attribute {
508///                         id: ast::Identifier {
509///                             name: "title"
510///                         },
511///                         value: ast::Pattern {
512///                             elements: vec![
513///                                 ast::PatternElement::TextElement {
514///                                     value: "This is a title"
515///                                 },
516///                             ]
517///                         }
518///                     },
519///                     ast::Attribute {
520///                         id: ast::Identifier {
521///                             name: "accesskey"
522///                         },
523///                         value: ast::Pattern {
524///                             elements: vec![
525///                                 ast::PatternElement::TextElement {
526///                                     value: "T"
527///                                 },
528///                             ]
529///                         }
530///                     }
531///                 ],
532///                 comment: None,
533///             }),
534///         ]
535///     }
536/// );
537/// ```
538#[derive(Debug, PartialEq, Clone)]
539#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
540pub struct Attribute<S> {
541    pub id: Identifier<S>,
542    pub value: Pattern<S>,
543}
544
545/// Identifier is part of nodes such as [`Message`], [`Term`] and [`Attribute`].
546///
547/// It is used to associate a unique key with an [`Entry`] or an [`Attribute`]
548/// and in [`Expression`] nodes to refer to another entry.
549///
550/// # Example
551///
552/// ```
553/// use fluent_syntax::parser;
554/// use fluent_syntax::ast;
555///
556/// let ftl = r#"
557///
558/// hello-world = Value
559///
560/// "#;
561///
562/// let resource = parser::parse(ftl)
563///     .expect("Failed to parse an FTL resource.");
564///
565/// assert_eq!(
566///     resource,
567///     ast::Resource {
568///         body: vec![
569///             ast::Entry::Message(ast::Message {
570///                 id: ast::Identifier {
571///                     name: "hello-world"
572///                 },
573///                 value: Some(ast::Pattern {
574///                     elements: vec![
575///                         ast::PatternElement::TextElement {
576///                             value: "Value"
577///                         }
578///                     ]
579///                 }),
580///                 attributes: vec![],
581///                 comment: None,
582///             }),
583///         ]
584///     }
585/// );
586/// ```
587#[derive(Debug, PartialEq, Eq, Clone)]
588#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
589pub struct Identifier<S> {
590    pub name: S,
591}
592
593/// Variant is a single branch of a value in a [`Select`](Expression::Select) expression.
594///
595/// It's a pair of [`VariantKey`] and [`Pattern`]. If the selector match the
596/// key, then the value of the variant is returned as the value of the expression.
597///
598/// # Example
599///
600/// ```
601/// use fluent_syntax::parser;
602/// use fluent_syntax::ast;
603///
604/// let ftl = r#"
605///
606/// hello-world = { $var ->
607///     [key1] Value 1
608///    *[other] Value 2
609/// }
610///
611/// "#;
612///
613/// let resource = parser::parse(ftl)
614///     .expect("Failed to parse an FTL resource.");
615///
616/// assert_eq!(
617///     resource,
618///     ast::Resource {
619///         body: vec![
620///             ast::Entry::Message(ast::Message {
621///                 id: ast::Identifier {
622///                     name: "hello-world"
623///                 },
624///                 value: Some(ast::Pattern {
625///                     elements: vec![
626///                         ast::PatternElement::Placeable {
627///                             expression: ast::Expression::Select {
628///                                 selector: ast::InlineExpression::VariableReference {
629///                                     id: ast::Identifier { name: "var" },
630///                                 },
631///                                 variants: vec![
632///                                     ast::Variant {
633///                                         key: ast::VariantKey::Identifier {
634///                                             name: "key1"
635///                                         },
636///                                         value: ast::Pattern {
637///                                             elements: vec![
638///                                                 ast::PatternElement::TextElement {
639///                                                     value: "Value 1",
640///                                                 }
641///                                             ]
642///                                         },
643///                                         default: false,
644///                                     },
645///                                     ast::Variant {
646///                                         key: ast::VariantKey::Identifier {
647///                                             name: "other"
648///                                         },
649///                                         value: ast::Pattern {
650///                                             elements: vec![
651///                                                 ast::PatternElement::TextElement {
652///                                                     value: "Value 2",
653///                                                 }
654///                                             ]
655///                                         },
656///                                         default: true,
657///                                     },
658///                                 ]
659///                             }
660///                         }
661///                     ]
662///                 }),
663///                 attributes: vec![],
664///                 comment: None,
665///             }),
666///         ]
667///     }
668/// );
669/// ```
670#[derive(Debug, PartialEq, Clone)]
671#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
672#[cfg_attr(feature = "serde", serde(tag = "type"))]
673pub struct Variant<S> {
674    pub key: VariantKey<S>,
675    pub value: Pattern<S>,
676    pub default: bool,
677}
678
679/// A key of a [`Variant`].
680///
681/// Variant key can be either an identifier or a number.
682///
683/// # Example
684///
685/// ```
686/// use fluent_syntax::parser;
687/// use fluent_syntax::ast;
688///
689/// let ftl = r#"
690///
691/// hello-world = { $var ->
692///     [0] Value 1
693///    *[other] Value 2
694/// }
695///
696/// "#;
697///
698/// let resource = parser::parse(ftl)
699///     .expect("Failed to parse an FTL resource.");
700///
701/// assert_eq!(
702///     resource,
703///     ast::Resource {
704///         body: vec![
705///             ast::Entry::Message(ast::Message {
706///                 id: ast::Identifier {
707///                     name: "hello-world"
708///                 },
709///                 value: Some(ast::Pattern {
710///                     elements: vec![
711///                         ast::PatternElement::Placeable {
712///                             expression: ast::Expression::Select {
713///                                 selector: ast::InlineExpression::VariableReference {
714///                                     id: ast::Identifier { name: "var" },
715///                                 },
716///                                 variants: vec![
717///                                     ast::Variant {
718///                                         key: ast::VariantKey::NumberLiteral {
719///                                             value: "0"
720///                                         },
721///                                         value: ast::Pattern {
722///                                             elements: vec![
723///                                                 ast::PatternElement::TextElement {
724///                                                     value: "Value 1",
725///                                                 }
726///                                             ]
727///                                         },
728///                                         default: false,
729///                                     },
730///                                     ast::Variant {
731///                                         key: ast::VariantKey::Identifier {
732///                                             name: "other"
733///                                         },
734///                                         value: ast::Pattern {
735///                                             elements: vec![
736///                                                 ast::PatternElement::TextElement {
737///                                                     value: "Value 2",
738///                                                 }
739///                                             ]
740///                                         },
741///                                         default: true,
742///                                     },
743///                                 ]
744///                             }
745///                         }
746///                     ]
747///                 }),
748///                 attributes: vec![],
749///                 comment: None,
750///             }),
751///         ]
752///     }
753/// );
754/// ```
755#[derive(Debug, PartialEq, Eq, Clone)]
756#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
757#[cfg_attr(feature = "serde", serde(tag = "type"))]
758pub enum VariantKey<S> {
759    Identifier { name: S },
760    NumberLiteral { value: S },
761}
762
763/// Fluent [`Comment`].
764///
765/// In Fluent, comments may be standalone, or associated with
766/// an entry such as [`Term`] or [`Message`].
767///
768/// When used as a standalone [`Entry`], comments may appear in one of
769/// three levels:
770///
771/// * Standalone comment
772/// * Group comment associated with a group of messages
773/// * Resource comment associated with the whole resource
774///
775/// # Example
776///
777/// ```
778/// use fluent_syntax::parser;
779/// use fluent_syntax::ast;
780///
781/// let ftl = r#"
782/// ## A standalone level comment
783/// "#;
784///
785/// let resource = parser::parse(ftl)
786///     .expect("Failed to parse an FTL resource.");
787///
788/// assert_eq!(
789///     resource,
790///     ast::Resource {
791///         body: vec![
792///             ast::Entry::Comment(ast::Comment {
793///                 content: vec![
794///                     "A standalone level comment"
795///                 ]
796///             })
797///         ]
798///     }
799/// );
800/// ```
801#[derive(Debug, PartialEq, Eq, Clone)]
802#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
803#[cfg_attr(feature = "serde", serde(from = "helper::CommentDef<S>"))]
804pub struct Comment<S> {
805    pub content: Vec<S>,
806}
807
808/// List of arguments for a [`FunctionReference`](InlineExpression::FunctionReference) or a
809/// [`TermReference`](InlineExpression::TermReference).
810///
811/// Function and Term reference may contain a list of positional and
812/// named arguments passed to them.
813///
814/// # Example
815///
816/// ```
817/// use fluent_syntax::parser;
818/// use fluent_syntax::ast;
819///
820/// let ftl = r#"
821///
822/// key = { FUNC($var1, "literal", style: "long") }
823///
824/// "#;
825///
826/// let resource = parser::parse(ftl)
827///     .expect("Failed to parse an FTL resource.");
828///
829/// assert_eq!(
830///     resource,
831///     ast::Resource {
832///         body: vec![
833///             ast::Entry::Message(
834///                 ast::Message {
835///                     id: ast::Identifier {
836///                         name: "key"
837///                     },
838///                     value: Some(ast::Pattern {
839///                         elements: vec![
840///                             ast::PatternElement::Placeable {
841///                                 expression: ast::Expression::Inline(
842///                                     ast::InlineExpression::FunctionReference {
843///                                         id: ast::Identifier {
844///                                             name: "FUNC"
845///                                         },
846///                                         arguments: ast::CallArguments {
847///                                             positional: vec![
848///                                                 ast::InlineExpression::VariableReference {
849///                                                     id: ast::Identifier {
850///                                                         name: "var1"
851///                                                     }
852///                                                 },
853///                                                 ast::InlineExpression::StringLiteral {
854///                                                     value: "literal",
855///                                                 }
856///                                             ],
857///                                             named: vec![
858///                                                 ast::NamedArgument {
859///                                                     name: ast::Identifier {
860///                                                         name: "style"
861///                                                     },
862///                                                     value: ast::InlineExpression::StringLiteral
863///                                                     {
864///                                                         value: "long"
865///                                                     }
866///                                                 }
867///                                             ],
868///                                         }
869///                                     }
870///                                 )
871///                             },
872///                         ]
873///                     }),
874///                     attributes: vec![],
875///                     comment: None,
876///                 }
877///             )
878///         ]
879///     }
880/// );
881/// ```
882#[derive(Debug, PartialEq, Clone, Default)]
883#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
884#[cfg_attr(feature = "serde", serde(tag = "type"))]
885pub struct CallArguments<S> {
886    pub positional: Vec<InlineExpression<S>>,
887    pub named: Vec<NamedArgument<S>>,
888}
889
890/// A key-value pair used in [`CallArguments`].
891///
892/// # Example
893///
894/// ```
895/// use fluent_syntax::parser;
896/// use fluent_syntax::ast;
897///
898/// let ftl = r#"
899///
900/// key = { FUNC(style: "long") }
901///
902/// "#;
903///
904/// let resource = parser::parse(ftl)
905///     .expect("Failed to parse an FTL resource.");
906///
907/// assert_eq!(
908///     resource,
909///     ast::Resource {
910///         body: vec![
911///             ast::Entry::Message(
912///                 ast::Message {
913///                     id: ast::Identifier {
914///                         name: "key"
915///                     },
916///                     value: Some(ast::Pattern {
917///                         elements: vec![
918///                             ast::PatternElement::Placeable {
919///                                 expression: ast::Expression::Inline(
920///                                     ast::InlineExpression::FunctionReference {
921///                                         id: ast::Identifier {
922///                                             name: "FUNC"
923///                                         },
924///                                         arguments: ast::CallArguments {
925///                                             positional: vec![],
926///                                             named: vec![
927///                                                 ast::NamedArgument {
928///                                                     name: ast::Identifier {
929///                                                         name: "style"
930///                                                     },
931///                                                     value: ast::InlineExpression::StringLiteral
932///                                                     {
933///                                                         value: "long"
934///                                                     }
935///                                                 }
936///                                             ],
937///                                         }
938///                                     }
939///                                 )
940///                             },
941///                         ]
942///                     }),
943///                     attributes: vec![],
944///                     comment: None,
945///                 }
946///             )
947///         ]
948///     }
949/// );
950/// ```
951#[derive(Debug, PartialEq, Clone)]
952#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
953#[cfg_attr(feature = "serde", serde(tag = "type"))]
954pub struct NamedArgument<S> {
955    pub name: Identifier<S>,
956    pub value: InlineExpression<S>,
957}
958
959/// A subset of expressions which can be used as [`Placeable`](PatternElement::Placeable),
960/// [`selector`](Expression::Select), or in [`CallArguments`].
961///
962/// # Example
963///
964/// ```
965/// use fluent_syntax::parser;
966/// use fluent_syntax::ast;
967///
968/// let ftl = r#"
969///
970/// key = { $emailCount }
971///
972/// "#;
973///
974/// let resource = parser::parse(ftl)
975///     .expect("Failed to parse an FTL resource.");
976///
977/// assert_eq!(
978///     resource,
979///     ast::Resource {
980///         body: vec![
981///             ast::Entry::Message(
982///                 ast::Message {
983///                     id: ast::Identifier {
984///                         name: "key"
985///                     },
986///                     value: Some(ast::Pattern {
987///                         elements: vec![
988///                             ast::PatternElement::Placeable {
989///                                 expression: ast::Expression::Inline(
990///                                     ast::InlineExpression::VariableReference {
991///                                         id: ast::Identifier {
992///                                             name: "emailCount"
993///                                         },
994///                                     }
995///                                 )
996///                             },
997///                         ]
998///                     }),
999///                     attributes: vec![],
1000///                     comment: None,
1001///                 }
1002///             )
1003///         ]
1004///     }
1005/// );
1006/// ```
1007#[derive(Debug, PartialEq, Clone)]
1008#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1009#[cfg_attr(feature = "serde", serde(tag = "type"))]
1010pub enum InlineExpression<S> {
1011    /// Single line string literal enclosed in `"`.
1012    ///
1013    /// # Example
1014    ///
1015    /// ```
1016    /// use fluent_syntax::parser;
1017    /// use fluent_syntax::ast;
1018    ///
1019    /// let ftl = r#"
1020    ///
1021    /// key = { "this is a literal" }
1022    ///
1023    /// "#;
1024    ///
1025    /// let resource = parser::parse(ftl)
1026    ///     .expect("Failed to parse an FTL resource.");
1027    ///
1028    /// assert_eq!(
1029    ///     resource,
1030    ///     ast::Resource {
1031    ///         body: vec![
1032    ///             ast::Entry::Message(
1033    ///                 ast::Message {
1034    ///                     id: ast::Identifier {
1035    ///                         name: "key"
1036    ///                     },
1037    ///                     value: Some(ast::Pattern {
1038    ///                         elements: vec![
1039    ///                             ast::PatternElement::Placeable {
1040    ///                                 expression: ast::Expression::Inline(
1041    ///                                     ast::InlineExpression::StringLiteral {
1042    ///                                         value: "this is a literal",
1043    ///                                     }
1044    ///                                 )
1045    ///                             },
1046    ///                         ]
1047    ///                     }),
1048    ///                     attributes: vec![],
1049    ///                     comment: None,
1050    ///                 }
1051    ///             )
1052    ///         ]
1053    ///     }
1054    /// );
1055    /// ```
1056    StringLiteral { value: S },
1057    /// A number literal.
1058    ///
1059    /// # Example
1060    ///
1061    /// ```
1062    /// use fluent_syntax::parser;
1063    /// use fluent_syntax::ast;
1064    ///
1065    /// let ftl = r#"
1066    ///
1067    /// key = { -0.5 }
1068    ///
1069    /// "#;
1070    ///
1071    /// let resource = parser::parse(ftl)
1072    ///     .expect("Failed to parse an FTL resource.");
1073    ///
1074    /// assert_eq!(
1075    ///     resource,
1076    ///     ast::Resource {
1077    ///         body: vec![
1078    ///             ast::Entry::Message(
1079    ///                 ast::Message {
1080    ///                     id: ast::Identifier {
1081    ///                         name: "key"
1082    ///                     },
1083    ///                     value: Some(ast::Pattern {
1084    ///                         elements: vec![
1085    ///                             ast::PatternElement::Placeable {
1086    ///                                 expression: ast::Expression::Inline(
1087    ///                                     ast::InlineExpression::NumberLiteral {
1088    ///                                         value: "-0.5",
1089    ///                                     }
1090    ///                                 )
1091    ///                             },
1092    ///                         ]
1093    ///                     }),
1094    ///                     attributes: vec![],
1095    ///                     comment: None,
1096    ///                 }
1097    ///             )
1098    ///         ]
1099    ///     }
1100    /// );
1101    /// ```
1102    NumberLiteral { value: S },
1103    /// A function reference.
1104    ///
1105    /// # Example
1106    ///
1107    /// ```
1108    /// use fluent_syntax::parser;
1109    /// use fluent_syntax::ast;
1110    ///
1111    /// let ftl = r#"
1112    ///
1113    /// key = { FUNC() }
1114    ///
1115    /// "#;
1116    ///
1117    /// let resource = parser::parse(ftl)
1118    ///     .expect("Failed to parse an FTL resource.");
1119    ///
1120    /// assert_eq!(
1121    ///     resource,
1122    ///     ast::Resource {
1123    ///         body: vec![
1124    ///             ast::Entry::Message(
1125    ///                 ast::Message {
1126    ///                     id: ast::Identifier {
1127    ///                         name: "key"
1128    ///                     },
1129    ///                     value: Some(ast::Pattern {
1130    ///                         elements: vec![
1131    ///                             ast::PatternElement::Placeable {
1132    ///                                 expression: ast::Expression::Inline(
1133    ///                                     ast::InlineExpression::FunctionReference {
1134    ///                                         id: ast::Identifier {
1135    ///                                             name: "FUNC"
1136    ///                                         },
1137    ///                                         arguments: ast::CallArguments::default(),
1138    ///                                     }
1139    ///                                 )
1140    ///                             },
1141    ///                         ]
1142    ///                     }),
1143    ///                     attributes: vec![],
1144    ///                     comment: None,
1145    ///                 }
1146    ///             )
1147    ///         ]
1148    ///     }
1149    /// );
1150    /// ```
1151    FunctionReference {
1152        id: Identifier<S>,
1153        arguments: CallArguments<S>,
1154    },
1155    /// A reference to another message.
1156    ///
1157    /// # Example
1158    ///
1159    /// ```
1160    /// use fluent_syntax::parser;
1161    /// use fluent_syntax::ast;
1162    ///
1163    /// let ftl = r#"
1164    ///
1165    /// key = { key2 }
1166    ///
1167    /// "#;
1168    ///
1169    /// let resource = parser::parse(ftl)
1170    ///     .expect("Failed to parse an FTL resource.");
1171    ///
1172    /// assert_eq!(
1173    ///     resource,
1174    ///     ast::Resource {
1175    ///         body: vec![
1176    ///             ast::Entry::Message(
1177    ///                 ast::Message {
1178    ///                     id: ast::Identifier {
1179    ///                         name: "key"
1180    ///                     },
1181    ///                     value: Some(ast::Pattern {
1182    ///                         elements: vec![
1183    ///                             ast::PatternElement::Placeable {
1184    ///                                 expression: ast::Expression::Inline(
1185    ///                                     ast::InlineExpression::MessageReference {
1186    ///                                         id: ast::Identifier {
1187    ///                                             name: "key2"
1188    ///                                         },
1189    ///                                         attribute: None,
1190    ///                                     }
1191    ///                                 )
1192    ///                             },
1193    ///                         ]
1194    ///                     }),
1195    ///                     attributes: vec![],
1196    ///                     comment: None,
1197    ///                 }
1198    ///             )
1199    ///         ]
1200    ///     }
1201    /// );
1202    /// ```
1203    MessageReference {
1204        id: Identifier<S>,
1205        attribute: Option<Identifier<S>>,
1206    },
1207    /// A reference to a term.
1208    ///
1209    /// # Example
1210    ///
1211    /// ```
1212    /// use fluent_syntax::parser;
1213    /// use fluent_syntax::ast;
1214    ///
1215    /// let ftl = r#"
1216    ///
1217    /// key = { -brand-name }
1218    ///
1219    /// "#;
1220    ///
1221    /// let resource = parser::parse(ftl)
1222    ///     .expect("Failed to parse an FTL resource.");
1223    ///
1224    /// assert_eq!(
1225    ///     resource,
1226    ///     ast::Resource {
1227    ///         body: vec![
1228    ///             ast::Entry::Message(
1229    ///                 ast::Message {
1230    ///                     id: ast::Identifier {
1231    ///                         name: "key"
1232    ///                     },
1233    ///                     value: Some(ast::Pattern {
1234    ///                         elements: vec![
1235    ///                             ast::PatternElement::Placeable {
1236    ///                                 expression: ast::Expression::Inline(
1237    ///                                     ast::InlineExpression::TermReference {
1238    ///                                         id: ast::Identifier {
1239    ///                                             name: "brand-name"
1240    ///                                         },
1241    ///                                         attribute: None,
1242    ///                                         arguments: None,
1243    ///                                     }
1244    ///                                 )
1245    ///                             },
1246    ///                         ]
1247    ///                     }),
1248    ///                     attributes: vec![],
1249    ///                     comment: None,
1250    ///                 }
1251    ///             )
1252    ///         ]
1253    ///     }
1254    /// );
1255    /// ```
1256    TermReference {
1257        id: Identifier<S>,
1258        attribute: Option<Identifier<S>>,
1259        arguments: Option<CallArguments<S>>,
1260    },
1261    /// A reference to a variable.
1262    ///
1263    /// # Example
1264    ///
1265    /// ```
1266    /// use fluent_syntax::parser;
1267    /// use fluent_syntax::ast;
1268    ///
1269    /// let ftl = r#"
1270    ///
1271    /// key = { $var1 }
1272    ///
1273    /// "#;
1274    ///
1275    /// let resource = parser::parse(ftl)
1276    ///     .expect("Failed to parse an FTL resource.");
1277    ///
1278    /// assert_eq!(
1279    ///     resource,
1280    ///     ast::Resource {
1281    ///         body: vec![
1282    ///             ast::Entry::Message(
1283    ///                 ast::Message {
1284    ///                     id: ast::Identifier {
1285    ///                         name: "key"
1286    ///                     },
1287    ///                     value: Some(ast::Pattern {
1288    ///                         elements: vec![
1289    ///                             ast::PatternElement::Placeable {
1290    ///                                 expression: ast::Expression::Inline(
1291    ///                                     ast::InlineExpression::VariableReference {
1292    ///                                         id: ast::Identifier {
1293    ///                                             name: "var1"
1294    ///                                         },
1295    ///                                     }
1296    ///                                 )
1297    ///                             },
1298    ///                         ]
1299    ///                     }),
1300    ///                     attributes: vec![],
1301    ///                     comment: None,
1302    ///                 }
1303    ///             )
1304    ///         ]
1305    ///     }
1306    /// );
1307    /// ```
1308    VariableReference { id: Identifier<S> },
1309    /// A placeable which may contain another expression.
1310    ///
1311    /// # Example
1312    ///
1313    /// ```
1314    /// use fluent_syntax::parser;
1315    /// use fluent_syntax::ast;
1316    ///
1317    /// let ftl = r#"
1318    ///
1319    /// key = { { "placeable" } }
1320    ///
1321    /// "#;
1322    ///
1323    /// let resource = parser::parse(ftl)
1324    ///     .expect("Failed to parse an FTL resource.");
1325    ///
1326    /// assert_eq!(
1327    ///     resource,
1328    ///     ast::Resource {
1329    ///         body: vec![
1330    ///             ast::Entry::Message(
1331    ///                 ast::Message {
1332    ///                     id: ast::Identifier {
1333    ///                         name: "key"
1334    ///                     },
1335    ///                     value: Some(ast::Pattern {
1336    ///                         elements: vec![
1337    ///                             ast::PatternElement::Placeable {
1338    ///                                 expression: ast::Expression::Inline(
1339    ///                                     ast::InlineExpression::Placeable {
1340    ///                                         expression: Box::new(
1341    ///                                             ast::Expression::Inline(
1342    ///                                                 ast::InlineExpression::StringLiteral {
1343    ///                                                     value: "placeable"
1344    ///                                                 }
1345    ///                                             )
1346    ///                                         )
1347    ///                                     }
1348    ///                                 )
1349    ///                             },
1350    ///                         ]
1351    ///                     }),
1352    ///                     attributes: vec![],
1353    ///                     comment: None,
1354    ///                 }
1355    ///             )
1356    ///         ]
1357    ///     }
1358    /// );
1359    /// ```
1360    Placeable { expression: Box<Expression<S>> },
1361}
1362
1363/// An expression that is either a select expression or an inline expression.
1364///
1365/// # Example
1366///
1367/// ```
1368/// use fluent_syntax::parser;
1369/// use fluent_syntax::ast;
1370///
1371/// let ftl = r#"
1372///
1373/// key = { $var ->
1374///     [key1] Value 1
1375///    *[other] Value 2
1376/// }
1377///
1378/// "#;
1379///
1380/// let resource = parser::parse(ftl)
1381///     .expect("Failed to parse an FTL resource.");
1382///
1383/// assert_eq!(
1384///     resource,
1385///     ast::Resource {
1386///         body: vec![
1387///             ast::Entry::Message(ast::Message {
1388///                 id: ast::Identifier {
1389///                     name: "key"
1390///                 },
1391///                 value: Some(ast::Pattern {
1392///                     elements: vec![
1393///                         ast::PatternElement::Placeable {
1394///                             expression: ast::Expression::Select {
1395///                                 selector: ast::InlineExpression::VariableReference {
1396///                                     id: ast::Identifier { name: "var" },
1397///                                 },
1398///                                 variants: vec![
1399///                                     ast::Variant {
1400///                                         key: ast::VariantKey::Identifier {
1401///                                             name: "key1"
1402///                                         },
1403///                                         value: ast::Pattern {
1404///                                             elements: vec![
1405///                                                 ast::PatternElement::TextElement {
1406///                                                     value: "Value 1",
1407///                                                 }
1408///                                             ]
1409///                                         },
1410///                                         default: false,
1411///                                     },
1412///                                     ast::Variant {
1413///                                         key: ast::VariantKey::Identifier {
1414///                                             name: "other"
1415///                                         },
1416///                                         value: ast::Pattern {
1417///                                             elements: vec![
1418///                                                 ast::PatternElement::TextElement {
1419///                                                     value: "Value 2",
1420///                                                 }
1421///                                             ]
1422///                                         },
1423///                                         default: true,
1424///                                     },
1425///                                 ]
1426///                             }
1427///                         }
1428///                     ]
1429///                 }),
1430///                 attributes: vec![],
1431///                 comment: None,
1432///             }),
1433///         ]
1434///     }
1435/// );
1436/// ```
1437#[derive(Debug, PartialEq, Clone)]
1438#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
1439#[cfg_attr(feature = "serde", serde(untagged))]
1440pub enum Expression<S> {
1441    /// A select expression such as:
1442    /// ```ftl
1443    /// key = { $var ->
1444    ///     [key1] Value 1
1445    ///    *[other] Value 2
1446    /// }
1447    /// ```
1448    Select {
1449        selector: InlineExpression<S>,
1450        variants: Vec<Variant<S>>,
1451    },
1452
1453    /// An inline expression such as `${ username }`:
1454    ///
1455    /// ```ftl
1456    /// hello-user = Hello ${ username }
1457    /// ```
1458    Inline(InlineExpression<S>),
1459}