1use super::scope::Scope;
2use super::{ResolveValue, ResolverError, WriteValue};
3
4use std::borrow::Borrow;
5use std::fmt;
6
7use fluent_syntax::ast;
8use fluent_syntax::unicode::{unescape_unicode, unescape_unicode_to_string};
9
10use crate::entry::GetEntry;
11use crate::memoizer::MemoizerKind;
12use crate::resource::FluentResource;
13use crate::types::FluentValue;
14
15impl<'p> WriteValue for ast::InlineExpression<&'p str> {
16 fn write<'scope, 'errors, W, R, M>(
17 &'scope self,
18 w: &mut W,
19 scope: &mut Scope<'scope, 'errors, R, M>,
20 ) -> fmt::Result
21 where
22 W: fmt::Write,
23 R: Borrow<FluentResource>,
24 M: MemoizerKind,
25 {
26 match self {
27 Self::StringLiteral { value } => unescape_unicode(w, value),
28 Self::MessageReference { id, attribute } => {
29 if let Some(msg) = scope.bundle.get_entry_message(id.name) {
30 if let Some(attr) = attribute {
31 msg.attributes
32 .iter()
33 .find_map(|a| {
34 if a.id.name == attr.name {
35 Some(scope.track(w, &a.value, self))
36 } else {
37 None
38 }
39 })
40 .unwrap_or_else(|| scope.write_ref_error(w, self))
41 } else {
42 msg.value
43 .as_ref()
44 .map(|value| scope.track(w, value, self))
45 .unwrap_or_else(|| {
46 scope.add_error(ResolverError::NoValue(id.name.to_string()));
47 w.write_char('{')?;
48 self.write_error(w)?;
49 w.write_char('}')
50 })
51 }
52 } else {
53 scope.write_ref_error(w, self)
54 }
55 }
56 Self::NumberLiteral { value } => FluentValue::try_number(*value).write(w, scope),
57 Self::TermReference {
58 id,
59 attribute,
60 arguments,
61 } => {
62 let (_, resolved_named_args) = scope.get_arguments(arguments.as_ref());
63
64 scope.local_args = Some(resolved_named_args);
65 let result = scope
66 .bundle
67 .get_entry_term(id.name)
68 .and_then(|term| {
69 if let Some(attr) = attribute {
70 term.attributes.iter().find_map(|a| {
71 if a.id.name == attr.name {
72 Some(scope.track(w, &a.value, self))
73 } else {
74 None
75 }
76 })
77 } else {
78 Some(scope.track(w, &term.value, self))
79 }
80 })
81 .unwrap_or_else(|| scope.write_ref_error(w, self));
82 scope.local_args = None;
83 result
84 }
85 Self::FunctionReference { id, arguments } => {
86 let (resolved_positional_args, resolved_named_args) =
87 scope.get_arguments(Some(arguments));
88
89 let func = scope.bundle.get_entry_function(id.name);
90
91 if let Some(func) = func {
92 let result = func(resolved_positional_args.as_slice(), &resolved_named_args);
93 if let FluentValue::Error = result {
94 self.write_error(w)
95 } else {
96 w.write_str(&result.as_string(scope))
97 }
98 } else {
99 scope.write_ref_error(w, self)
100 }
101 }
102 Self::VariableReference { id } => {
103 let args = scope.local_args.as_ref().or(scope.args);
104
105 if let Some(arg) = args.and_then(|args| args.get(id.name)) {
106 arg.write(w, scope)
107 } else {
108 if scope.local_args.is_none() {
109 scope.add_error(self.into());
110 }
111 w.write_char('{')?;
112 self.write_error(w)?;
113 w.write_char('}')
114 }
115 }
116 Self::Placeable { expression } => expression.write(w, scope),
117 }
118 }
119
120 fn write_error<W>(&self, w: &mut W) -> fmt::Result
121 where
122 W: fmt::Write,
123 {
124 match self {
125 Self::MessageReference {
126 id,
127 attribute: Some(attribute),
128 } => write!(w, "{}.{}", id.name, attribute.name),
129 Self::MessageReference {
130 id,
131 attribute: None,
132 } => w.write_str(id.name),
133 Self::TermReference {
134 id,
135 attribute: Some(attribute),
136 ..
137 } => write!(w, "-{}.{}", id.name, attribute.name),
138 Self::TermReference {
139 id,
140 attribute: None,
141 ..
142 } => write!(w, "-{}", id.name),
143 Self::FunctionReference { id, .. } => write!(w, "{}()", id.name),
144 Self::VariableReference { id } => write!(w, "${}", id.name),
145 _ => unreachable!(),
146 }
147 }
148}
149
150impl<'p> ResolveValue for ast::InlineExpression<&'p str> {
151 fn resolve<'source, 'errors, R, M>(
152 &'source self,
153 scope: &mut Scope<'source, 'errors, R, M>,
154 ) -> FluentValue<'source>
155 where
156 R: Borrow<FluentResource>,
157 M: MemoizerKind,
158 {
159 match self {
160 Self::StringLiteral { value } => unescape_unicode_to_string(value).into(),
161 Self::NumberLiteral { value } => FluentValue::try_number(*value),
162 Self::VariableReference { id } => {
163 let args = scope.local_args.as_ref().or(scope.args);
164
165 if let Some(arg) = args.and_then(|args| args.get(id.name)) {
166 arg.clone()
167 } else {
168 if scope.local_args.is_none() {
169 scope.add_error(self.into());
170 }
171 FluentValue::Error
172 }
173 }
174 _ => {
175 let mut result = String::new();
176 self.write(&mut result, scope).expect("Failed to write");
177 result.into()
178 }
179 }
180 }
181}
182