summaryrefslogtreecommitdiffstats
path: root/third_party/rust/fluent-bundle/src/resolver/inline_expression.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/fluent-bundle/src/resolver/inline_expression.rs')
-rw-r--r--third_party/rust/fluent-bundle/src/resolver/inline_expression.rs181
1 files changed, 181 insertions, 0 deletions
diff --git a/third_party/rust/fluent-bundle/src/resolver/inline_expression.rs b/third_party/rust/fluent-bundle/src/resolver/inline_expression.rs
new file mode 100644
index 0000000000..b9e89b6e8e
--- /dev/null
+++ b/third_party/rust/fluent-bundle/src/resolver/inline_expression.rs
@@ -0,0 +1,181 @@
+use super::scope::Scope;
+use super::{ResolveValue, ResolverError, WriteValue};
+
+use std::borrow::Borrow;
+use std::fmt;
+
+use fluent_syntax::ast;
+use fluent_syntax::unicode::{unescape_unicode, unescape_unicode_to_string};
+
+use crate::entry::GetEntry;
+use crate::memoizer::MemoizerKind;
+use crate::resource::FluentResource;
+use crate::types::FluentValue;
+
+impl<'p> WriteValue for ast::InlineExpression<&'p str> {
+ fn write<'scope, 'errors, W, R, M>(
+ &'scope self,
+ w: &mut W,
+ scope: &mut Scope<'scope, 'errors, R, M>,
+ ) -> fmt::Result
+ where
+ W: fmt::Write,
+ R: Borrow<FluentResource>,
+ M: MemoizerKind,
+ {
+ match self {
+ Self::StringLiteral { value } => unescape_unicode(w, value),
+ Self::MessageReference { id, attribute } => {
+ if let Some(msg) = scope.bundle.get_entry_message(id.name) {
+ if let Some(attr) = attribute {
+ msg.attributes
+ .iter()
+ .find_map(|a| {
+ if a.id.name == attr.name {
+ Some(scope.track(w, &a.value, self))
+ } else {
+ None
+ }
+ })
+ .unwrap_or_else(|| scope.write_ref_error(w, self))
+ } else {
+ msg.value
+ .as_ref()
+ .map(|value| scope.track(w, value, self))
+ .unwrap_or_else(|| {
+ scope.add_error(ResolverError::NoValue(id.name.to_string()));
+ w.write_char('{')?;
+ self.write_error(w)?;
+ w.write_char('}')
+ })
+ }
+ } else {
+ scope.write_ref_error(w, self)
+ }
+ }
+ Self::NumberLiteral { value } => FluentValue::try_number(*value).write(w, scope),
+ Self::TermReference {
+ id,
+ attribute,
+ arguments,
+ } => {
+ let (_, resolved_named_args) = scope.get_arguments(arguments.as_ref());
+
+ scope.local_args = Some(resolved_named_args);
+ let result = scope
+ .bundle
+ .get_entry_term(id.name)
+ .and_then(|term| {
+ if let Some(attr) = attribute {
+ term.attributes.iter().find_map(|a| {
+ if a.id.name == attr.name {
+ Some(scope.track(w, &a.value, self))
+ } else {
+ None
+ }
+ })
+ } else {
+ Some(scope.track(w, &term.value, self))
+ }
+ })
+ .unwrap_or_else(|| scope.write_ref_error(w, self));
+ scope.local_args = None;
+ result
+ }
+ Self::FunctionReference { id, arguments } => {
+ let (resolved_positional_args, resolved_named_args) =
+ scope.get_arguments(Some(arguments));
+
+ let func = scope.bundle.get_entry_function(id.name);
+
+ if let Some(func) = func {
+ let result = func(resolved_positional_args.as_slice(), &resolved_named_args);
+ if let FluentValue::Error = result {
+ self.write_error(w)
+ } else {
+ w.write_str(&result.as_string(scope))
+ }
+ } else {
+ scope.write_ref_error(w, self)
+ }
+ }
+ Self::VariableReference { id } => {
+ let args = scope.local_args.as_ref().or(scope.args);
+
+ if let Some(arg) = args.and_then(|args| args.get(id.name)) {
+ arg.write(w, scope)
+ } else {
+ if scope.local_args.is_none() {
+ scope.add_error(self.into());
+ }
+ w.write_char('{')?;
+ self.write_error(w)?;
+ w.write_char('}')
+ }
+ }
+ Self::Placeable { expression } => expression.write(w, scope),
+ }
+ }
+
+ fn write_error<W>(&self, w: &mut W) -> fmt::Result
+ where
+ W: fmt::Write,
+ {
+ match self {
+ Self::MessageReference {
+ id,
+ attribute: Some(attribute),
+ } => write!(w, "{}.{}", id.name, attribute.name),
+ Self::MessageReference {
+ id,
+ attribute: None,
+ } => w.write_str(id.name),
+ Self::TermReference {
+ id,
+ attribute: Some(attribute),
+ ..
+ } => write!(w, "-{}.{}", id.name, attribute.name),
+ Self::TermReference {
+ id,
+ attribute: None,
+ ..
+ } => write!(w, "-{}", id.name),
+ Self::FunctionReference { id, .. } => write!(w, "{}()", id.name),
+ Self::VariableReference { id } => write!(w, "${}", id.name),
+ _ => unreachable!(),
+ }
+ }
+}
+
+impl<'p> ResolveValue for ast::InlineExpression<&'p str> {
+ fn resolve<'source, 'errors, R, M>(
+ &'source self,
+ scope: &mut Scope<'source, 'errors, R, M>,
+ ) -> FluentValue<'source>
+ where
+ R: Borrow<FluentResource>,
+ M: MemoizerKind,
+ {
+ match self {
+ Self::StringLiteral { value } => unescape_unicode_to_string(value).into(),
+ Self::NumberLiteral { value } => FluentValue::try_number(*value),
+ Self::VariableReference { id } => {
+ let args = scope.local_args.as_ref().or(scope.args);
+
+ if let Some(arg) = args.and_then(|args| args.get(id.name)) {
+ arg.clone()
+ } else {
+ if scope.local_args.is_none() {
+ scope.add_error(self.into());
+ }
+ FluentValue::Error
+ }
+ }
+ _ => {
+ let mut result = String::new();
+ self.write(&mut result, scope).expect("Failed to write");
+ result.into()
+ }
+ }
+ }
+}