| 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<'bundle> WriteValue<'bundle> for ast::InlineExpression<&'bundle str> { |
| 16 | fn write<'ast, 'args, 'errors, W, R, M>( |
| 17 | &'ast self, |
| 18 | w: &mut W, |
| 19 | scope: &mut Scope<'bundle, 'ast, 'args, '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.into_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<'bundle> ResolveValue<'bundle> for ast::InlineExpression<&'bundle str> { |
| 151 | fn resolve<'ast, 'args, 'errors, R, M>( |
| 152 | &'ast self, |
| 153 | scope: &mut Scope<'bundle, 'ast, 'args, 'errors, R, M>, |
| 154 | ) -> FluentValue<'bundle> |
| 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 | if let Some(local_args) = &scope.local_args { |
| 164 | if let Some(arg) = local_args.get(id.name) { |
| 165 | return arg.clone(); |
| 166 | } |
| 167 | } else if let Some(arg) = scope.args.and_then(|args| args.get(id.name)) { |
| 168 | return arg.into_owned(); |
| 169 | } |
| 170 | |
| 171 | if scope.local_args.is_none() { |
| 172 | scope.add_error(self.into()); |
| 173 | } |
| 174 | FluentValue::Error |
| 175 | } |
| 176 | Self::FunctionReference { id, arguments } => { |
| 177 | let (resolved_positional_args, resolved_named_args) = |
| 178 | scope.get_arguments(Some(arguments)); |
| 179 | |
| 180 | let func = scope.bundle.get_entry_function(id.name); |
| 181 | |
| 182 | if let Some(func) = func { |
| 183 | let result = func(resolved_positional_args.as_slice(), &resolved_named_args); |
| 184 | result |
| 185 | } else { |
| 186 | FluentValue::Error |
| 187 | } |
| 188 | } |
| 189 | _ => { |
| 190 | let mut result = String::new(); |
| 191 | self.write(&mut result, scope).expect("Failed to write" ); |
| 192 | result.into() |
| 193 | } |
| 194 | } |
| 195 | } |
| 196 | } |
| 197 | |