Skip to content

Commit

Permalink
refactor(fluent-bundle): Remove WriteValue and ResolveValue traits
Browse files Browse the repository at this point in the history
  • Loading branch information
JasperDeSutter authored and alerque committed May 6, 2024
1 parent af86dfb commit 1ee2a7c
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 110 deletions.
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
40 changes: 9 additions & 31 deletions fluent-bundle/src/resolver/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
//! The `resolver` module contains the definitions and implementations for the internal
//! `ResolveValue` and `WriteValue` traits. The former converts AST nodes to a
//! [`FluentValue`], and the latter converts them to a string that is written to an
//! implementor of the [`std::fmt::Write`] trait.
//! `WriteOrResolve` and `WriteOrResolveContext` traits.
//! There is an implementation that resolves AST nodes to a [`FluentValue`], and one
//! that writes to an implementor of the [`std::fmt::Write`] trait.

pub mod errors;
mod expression;
mod inline_expression;
pub mod pattern;
pub(crate) mod pattern;
mod scope;

pub use errors::ResolverError;
Expand All @@ -21,31 +21,7 @@ use crate::memoizer::MemoizerKind;
use crate::resource::FluentResource;
use crate::types::FluentValue;

/// Resolves an AST node to a [`FluentValue`].
pub(crate) trait ResolveValue<'bundle> {
/// Resolves an AST node to a [`FluentValue`].
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;
}

/// Resolves an AST node to a string that is written to source `W`.
pub(crate) trait WriteValue<'bundle> {
/// Resolves an AST node to a string that is written to source `W`.
fn write<'ast, 'args, 'errors, W, R, M>(
&'ast self,
w: &mut W,
scope: &mut Scope<'bundle, 'ast, 'args, 'errors, R, M>,
) -> fmt::Result
where
W: fmt::Write,
R: Borrow<FluentResource>,
M: MemoizerKind;
}
use self::pattern::{resolve_pattern, write_pattern};

pub trait WriteOrResolveContext<'bundle> {
type Result;
Expand All @@ -71,6 +47,7 @@ pub trait WriteOrResolveContext<'bundle> {
M: MemoizerKind;
}

/// Resolves an AST node to a string that is written to source `W`.
impl<'bundle, W> WriteOrResolveContext<'bundle> for W
where
W: fmt::Write,
Expand Down Expand Up @@ -118,10 +95,11 @@ where
R: Borrow<FluentResource>,
M: MemoizerKind,
{
pattern.write(self, scope)
write_pattern(pattern, self, scope)
}
}

/// Resolves an AST node to a [`FluentValue`].
struct ResolveContext;

impl<'bundle> WriteOrResolveContext<'bundle> for ResolveContext {
Expand Down Expand Up @@ -156,7 +134,7 @@ impl<'bundle> WriteOrResolveContext<'bundle> for ResolveContext {
R: Borrow<FluentResource>,
M: MemoizerKind,
{
pattern.resolve(scope)
resolve_pattern(pattern, scope)
}
}

Expand Down
138 changes: 64 additions & 74 deletions fluent-bundle/src/resolver/pattern.rs
Original file line number Diff line number Diff line change
@@ -1,101 +1,91 @@
use super::scope::Scope;
use super::{ResolverError, WriteValue};
use super::ResolverError;

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

use fluent_syntax::ast;

use crate::memoizer::MemoizerKind;
use crate::resolver::ResolveValue;
use crate::resource::FluentResource;
use crate::types::FluentValue;

const MAX_PLACEABLES: u8 = 100;

impl<'bundle> WriteValue<'bundle> for ast::Pattern<&'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
where
W: fmt::Write,
R: Borrow<FluentResource>,
M: MemoizerKind,
{
let len = self.elements.len();
pub fn write_pattern<'bundle, 'ast, 'args, 'errors, W, R, M>(
pattern: &'ast ast::Pattern<&'bundle str>,
w: &mut W,
scope: &mut Scope<'bundle, 'ast, 'args, 'errors, R, M>,
) -> fmt::Result
where
W: fmt::Write,
R: Borrow<FluentResource>,
M: MemoizerKind,
{
let len = pattern.elements.len();

for elem in &self.elements {
if scope.dirty {
return Ok(());
}
for elem in &pattern.elements {
if scope.dirty {
return Ok(());
}

match elem {
ast::PatternElement::TextElement { value } => {
if let Some(ref transform) = scope.bundle.transform {
w.write_str(&transform(value))?;
} else {
w.write_str(value)?;
}
match elem {
ast::PatternElement::TextElement { value } => {
if let Some(ref transform) = scope.bundle.transform {
w.write_str(&transform(value))?;
} else {
w.write_str(value)?;
}
}
ast::PatternElement::Placeable { ref expression } => {
scope.placeables += 1;
if scope.placeables > MAX_PLACEABLES {
scope.dirty = true;
scope.add_error(ResolverError::TooManyPlaceables);
return Ok(());
}
ast::PatternElement::Placeable { ref expression } => {
scope.placeables += 1;
if scope.placeables > MAX_PLACEABLES {
scope.dirty = true;
scope.add_error(ResolverError::TooManyPlaceables);
return Ok(());
}

let needs_isolation = scope.bundle.use_isolating
&& len > 1
&& !matches!(
expression,
ast::Expression::Inline(ast::InlineExpression::MessageReference { .. },)
| ast::Expression::Inline(
ast::InlineExpression::TermReference { .. },
)
| ast::Expression::Inline(
ast::InlineExpression::StringLiteral { .. },
)
);
if needs_isolation {
w.write_char('\u{2068}')?;
}
scope.maybe_track(w, self, expression)?;
if needs_isolation {
w.write_char('\u{2069}')?;
}
let needs_isolation = scope.bundle.use_isolating
&& len > 1
&& !matches!(
expression,
ast::Expression::Inline(ast::InlineExpression::MessageReference { .. },)
| ast::Expression::Inline(ast::InlineExpression::TermReference { .. },)
| ast::Expression::Inline(ast::InlineExpression::StringLiteral { .. },)
);
if needs_isolation {
w.write_char('\u{2068}')?;
}
scope.maybe_track(w, pattern, expression)?;
if needs_isolation {
w.write_char('\u{2069}')?;
}
}
}
Ok(())
}
Ok(())
}

impl<'bundle> ResolveValue<'bundle> for ast::Pattern<&'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,
{
let len = self.elements.len();
pub fn resolve_pattern<'bundle, 'ast, 'args, 'errors, R, M>(
pattern: &'ast ast::Pattern<&'bundle str>,
scope: &mut Scope<'bundle, 'ast, 'args, 'errors, R, M>,
) -> FluentValue<'bundle>
where
R: Borrow<FluentResource>,
M: MemoizerKind,
{
let len = pattern.elements.len();

if len == 1 {
if let ast::PatternElement::TextElement { value } = self.elements[0] {
return scope
.bundle
.transform
.map_or_else(|| value.into(), |transform| transform(value).into());
}
if len == 1 {
if let ast::PatternElement::TextElement { value } = pattern.elements[0] {
return scope
.bundle
.transform
.map_or_else(|| value.into(), |transform| transform(value).into());
}

let mut result = String::new();
self.write(&mut result, scope)
.expect("Failed to write to a string.");
result.into()
}

let mut result = String::new();
write_pattern(pattern, &mut result, scope).expect("Failed to write to a string.");
result.into()
}
2 changes: 1 addition & 1 deletion fluent-bundle/src/resolver/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::borrow::Borrow;

use super::{ResolveContext, ResolverError, WriteOrResolve, WriteOrResolveContext};

/// State for a single `ResolveValue::to_value` call.
/// State for a single `WriteOrResolve::write_or_resolve` call.
pub struct Scope<'bundle, 'ast, 'args, 'errors, R, M> {
/// The current `FluentBundle` instance.
pub bundle: &'bundle FluentBundle<R, M>,
Expand Down

0 comments on commit 1ee2a7c

Please sign in to comment.