1 | use super::scope::Scope; |
2 | use super::{ResolveValue, ResolverError, WriteValue}; |
3 | |
4 | use std::borrow::Borrow; |
5 | use std::fmt; |
6 | |
7 | use fluent_syntax::ast; |
8 | use fluent_syntax::unicode::{unescape_unicode, unescape_unicode_to_string}; |
9 | |
10 | use crate::entry::GetEntry; |
11 | use crate::memoizer::MemoizerKind; |
12 | use crate::resource::FluentResource; |
13 | use crate::types::FluentValue; |
14 | |
15 | impl<'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 | |
150 | impl<'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 | |