Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge ResolveValue and WriteValue traits #310

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions fluent-bundle/src/bundle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ use crate::entry::GetEntry;
use crate::errors::{EntryKind, FluentError};
use crate::memoizer::MemoizerKind;
use crate::message::FluentMessage;
use crate::resolver::{ResolveValue, Scope, WriteValue};
use crate::resolver::pattern::{resolve_pattern, write_pattern};
use crate::resolver::Scope;
use crate::resource::FluentResource;
use crate::types::FluentValue;

Expand Down Expand Up @@ -450,7 +451,7 @@ impl<R, M> FluentBundle<R, M> {
M: MemoizerKind,
{
let mut scope = Scope::new(self, args, Some(errors));
pattern.write(w, &mut scope)
write_pattern(pattern, w, &mut scope)
}

/// Formats a pattern which comes from a `FluentMessage`.
Expand Down Expand Up @@ -493,7 +494,7 @@ impl<R, M> FluentBundle<R, M> {
M: MemoizerKind,
{
let mut scope = Scope::new(self, args, Some(errors));
let value = pattern.resolve(&mut scope);
let value = resolve_pattern(pattern, &mut scope);
value.into_string(&scope)
}

Expand Down
2 changes: 1 addition & 1 deletion fluent-bundle/src/resolver/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ where
}

/// Errors generated during the process of resolving a fluent message into a string.
/// This process takes place in the `write` method of the `WriteValue` trait.
/// This process takes place in the `write_or_resolve` method of the `WriteOrResolve` trait.
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum ResolverError {
Reference(ReferenceKind),
Expand Down
28 changes: 14 additions & 14 deletions fluent-bundle/src/resolver/expression.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
use super::scope::Scope;
use super::WriteValue;
use super::{ResolveContext, WriteOrResolve, WriteOrResolveContext};

use std::borrow::Borrow;
use std::fmt;

use fluent_syntax::ast;

use crate::memoizer::MemoizerKind;
use crate::resolver::{ResolveValue, ResolverError};
use crate::resolver::ResolverError;
use crate::resource::FluentResource;
use crate::types::FluentValue;

impl<'bundle> WriteValue<'bundle> for ast::Expression<&'bundle str> {
fn write<'ast, 'args, 'errors, W, R, M>(
&'ast self,
w: &mut W,
scope: &mut Scope<'bundle, 'ast, 'args, 'errors, R, M>,
) -> fmt::Result
impl<'bundle> WriteOrResolve<'bundle> for ast::Expression<&'bundle str> {
fn write_or_resolve<'other, R, M, T>(
&'bundle self,
scope: &mut Scope<'bundle, 'other, R, M>,
context: &mut T,
) -> T::Result
where
W: fmt::Write,
R: Borrow<FluentResource>,
M: MemoizerKind,
T: WriteOrResolveContext<'bundle>,
{
match self {
Self::Inline(exp) => exp.write(w, scope),
Self::Inline(exp) => exp.write_or_resolve(scope, context),
Self::Select { selector, variants } => {
let selector = selector.resolve(scope);
let selector = selector.write_or_resolve(scope, &mut ResolveContext);
match selector {
FluentValue::String(_) | FluentValue::Number(_) => {
for variant in variants {
Expand All @@ -36,7 +36,7 @@ impl<'bundle> WriteValue<'bundle> for ast::Expression<&'bundle str> {
}
};
if key.matches(&selector, scope) {
return variant.value.write(w, scope);
return context.resolve_pattern(scope, &variant.value);
}
}
}
Expand All @@ -45,11 +45,11 @@ impl<'bundle> WriteValue<'bundle> for ast::Expression<&'bundle str> {

for variant in variants {
if variant.default {
return variant.value.write(w, scope);
return context.resolve_pattern(scope, &variant.value);
}
}
scope.add_error(ResolverError::MissingDefault);
Ok(())
context.error(self, true)
}
}
}
Expand Down
135 changes: 46 additions & 89 deletions fluent-bundle/src/resolver/inline_expression.rs
Original file line number Diff line number Diff line change
@@ -1,59 +1,60 @@
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 super::{ResolverError, WriteOrResolve, WriteOrResolveContext};

use crate::entry::GetEntry;
use crate::memoizer::MemoizerKind;
use crate::resource::FluentResource;
use crate::types::FluentValue;
use fluent_syntax::ast;
use std::borrow::{Borrow, Cow};
use std::fmt;

impl<'bundle> WriteValue<'bundle> for ast::InlineExpression<&'bundle str> {
fn write<'ast, 'args, 'errors, W, R, M>(
&'ast self,
w: &mut W,
scope: &mut Scope<'bundle, 'ast, 'args, 'errors, R, M>,
) -> fmt::Result
impl<'bundle> WriteOrResolve<'bundle> for ast::InlineExpression<&'bundle str> {
fn write_or_resolve<'other, R, M, T>(
&'bundle self,
scope: &mut Scope<'bundle, 'other, R, M>,
context: &mut T,
) -> T::Result
where
W: fmt::Write,
R: Borrow<FluentResource>,
M: MemoizerKind,
T: WriteOrResolveContext<'bundle>,
{
match self {
Self::StringLiteral { value } => unescape_unicode(w, value),
Self::StringLiteral { value } => context.unescape(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))
Some(scope.track(context, &a.value, self))
} else {
None
}
})
.unwrap_or_else(|| scope.write_ref_error(w, self))
.unwrap_or_else(|| {
scope.add_error(self.into());
context.error(self, true)
})
} else {
msg.value
.as_ref()
.map(|value| scope.track(w, value, self))
.map(|value| scope.track(context, value, self))
.unwrap_or_else(|| {
scope.add_error(ResolverError::NoValue(id.name.to_string()));
w.write_char('{')?;
self.write_error(w)?;
w.write_char('}')
context.error(self, true)
})
}
} else {
scope.write_ref_error(w, self)
scope.add_error(self.into());
context.error(self, true)
}
}
Self::NumberLiteral { value } => FluentValue::try_number(value).write(w, scope),
Self::NumberLiteral { value } => {
context.value(scope, Cow::Owned(FluentValue::try_number(value)))
}
Self::TermReference {
id,
attribute,
Expand All @@ -69,16 +70,19 @@ impl<'bundle> WriteValue<'bundle> for ast::InlineExpression<&'bundle str> {
if let Some(attr) = attribute {
term.attributes.iter().find_map(|a| {
if a.id.name == attr.name {
Some(scope.track(w, &a.value, self))
Some(scope.track(context, &a.value, self))
} else {
None
}
})
} else {
Some(scope.track(w, &term.value, self))
Some(scope.track(context, &term.value, self))
}
})
.unwrap_or_else(|| scope.write_ref_error(w, self));
.unwrap_or_else(|| {
scope.add_error(self.into());
context.error(self, true)
});
scope.local_args = None;
result
}
Expand All @@ -90,30 +94,31 @@ impl<'bundle> WriteValue<'bundle> for ast::InlineExpression<&'bundle str> {

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)
if matches!(result, FluentValue::Error) {
context.error(self, false)
} else {
w.write_str(&result.into_string(scope))
context.value(scope, Cow::Owned(result))
}
} else {
scope.write_ref_error(w, self)
scope.add_error(self.into());
context.error(self, true)
}
}
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());
if let Some(local_args) = &scope.local_args {
if let Some(arg) = local_args.get(id.name) {
return context.value(scope, Cow::Borrowed(arg));
}
w.write_char('{')?;
self.write_error(w)?;
w.write_char('}')
} else if let Some(arg) = scope.args.and_then(|args| args.get(id.name)) {
return context.value(scope, Cow::Owned(arg.into_owned()));
}

if scope.local_args.is_none() {
scope.add_error(self.into());
}
context.error(self, true)
}
Self::Placeable { expression } => expression.write(w, scope),
Self::Placeable { expression } => expression.write_or_resolve(scope, context),
}
}

Expand Down Expand Up @@ -146,51 +151,3 @@ impl<'bundle> WriteValue<'bundle> for ast::InlineExpression<&'bundle str> {
}
}
}

impl<'bundle> ResolveValue<'bundle> for ast::InlineExpression<&'bundle str> {
fn resolve<'ast, 'args, 'errors, R, M>(
&'ast self,
scope: &mut Scope<'bundle, 'ast, 'args, 'errors, R, M>,
) -> FluentValue<'bundle>
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 } => {
if let Some(local_args) = &scope.local_args {
if let Some(arg) = local_args.get(id.name) {
return arg.clone();
}
} else if let Some(arg) = scope.args.and_then(|args| args.get(id.name)) {
return arg.into_owned();
}

if scope.local_args.is_none() {
scope.add_error(self.into());
}
FluentValue::Error
}
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);
result
} else {
FluentValue::Error
}
}
_ => {
let mut result = String::new();
self.write(&mut result, scope).expect("Failed to write");
result.into()
}
}
}
}
Loading