1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use super::{Attribute, Identifier, Pattern};
#[cfg(feature = "walker")]
use crate::walker::{Visitor, Walkable, Walker};
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "hash", derive(Eq, PartialOrd, Ord, Hash))]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub enum MessageArguments {
Patterned(Pattern, Vec<Attribute>),
Plain(Vec<Attribute>),
}
impl MessageArguments {
pub fn pattern(&self) -> Option<&Pattern> {
match self {
Self::Patterned(pattern, _) => Some(pattern),
Self::Plain(_) => None,
}
}
pub fn attributes(&self) -> &[Attribute] {
match self {
Self::Patterned(_, attributes) => attributes.as_slice(),
Self::Plain(attributes) => attributes.as_slice(),
}
}
}
#[cfg(feature = "walker")]
impl Walkable for MessageArguments {
fn walk(&self, visitor: &mut dyn Visitor) {
match self {
Self::Patterned(pattern, attributes) => {
Walker::walk(pattern, visitor);
attributes.iter().for_each(|a| Walker::walk(a, visitor));
}
Self::Plain(attributes) => attributes.iter().for_each(|a| Walker::walk(a, visitor)),
}
}
}
impl std::fmt::Display for MessageArguments {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let value = match self {
Self::Patterned(pattern, attributes) => {
let attributes = attributes
.iter()
.map(|a| a.to_string())
.collect::<Vec<_>>()
.join("");
format!("{}{}", pattern, attributes)
}
Self::Plain(attributes) => attributes
.iter()
.map(|a| a.to_string())
.collect::<Vec<_>>()
.join(""),
};
write!(f, "{value}")
}
}
/// [Message](crate::ast::Message) ::= [Identifier](crate::ast::Identifier) blank_inline? "=" blank_inline? (([Pattern](crate::ast::Pattern) [Attribute](crate::ast::Attribute)*) | (Attribute+))
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "hash", derive(Eq, PartialOrd, Ord, Hash))]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct Message {
identifier: Identifier,
arguments: MessageArguments,
}
impl Message {
/// Constructs a new `Message` representing a translatable message in a Fluent Translation List (FTL) file.
/// A message consists of a unique identifier, a primary value pattern, and optional attributes,
/// a comment, or a select expression. The `arguments` parameter encapsulates all of these
/// optional components.
///
/// # Arguments
/// * `identifier` - The unique identifier (key) of the message (e.g., `hello-world`, `user-count`).
/// Must be a valid Fluent identifier.
/// * `arguments` - The optional components of the message, including:
/// - A comment (resource/group/section level)
/// - The primary value pattern
/// - Zero or more attributes
/// - An optional select expression (for plural/category selection)
pub fn new(identifier: Identifier, arguments: MessageArguments) -> Self {
Self {
identifier,
arguments,
}
}
/// Returns the message identifier.
///
/// Note: a [Message](crate::ast::Message) and [Term](crate::ast::Term) [Identifier](crate::ast::Identifier)
/// may be the same, e,g, `product = ...` versus `-product = ...`.
pub fn identifier(&self) -> &Identifier {
&self.identifier
}
/// Returns the message identifier _name_.
///
/// Note: Differentiates the [Message](crate::ast::Message) and [Term](crate::ast::Term)
/// [Identifier](crate::ast::Identifier) name by using the '-' prefix for the [Term](crate::ast::Term).
pub fn identifier_name(&self) -> String {
self.identifier.to_string()
}
/// Returns the message [Pattern](crate::ast::Pattern), if provided.
pub fn pattern(&self) -> Option<&Pattern> {
self.arguments.pattern()
}
/// Returns the message [Attribute](crate::ast::Attribute)s.
pub fn attributes(&self) -> &[Attribute] {
self.arguments.attributes()
}
}
#[cfg(feature = "walker")]
impl Walkable for Message {
fn walk(&self, visitor: &mut dyn Visitor) {
visitor.visit_message(self);
Walker::walk(&self.identifier, visitor);
Walker::walk(&self.arguments, visitor);
}
}
impl std::fmt::Display for Message {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "{} = {}", self.identifier, self.arguments)
}
}