| 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 |  | 
|---|