Skip to content

Commit 3e7aa82

Browse files
committed
Reorganize AST to FluentMessage into From impls.
1 parent 4959ad1 commit 3e7aa82

File tree

5 files changed

+115
-56
lines changed

5 files changed

+115
-56
lines changed

fluent-bundle/src/bundle.rs

Lines changed: 9 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use crate::entry::Entry;
1919
use crate::entry::GetEntry;
2020
use crate::errors::{EntryKind, FluentError};
2121
use crate::memoizer::MemoizerKind;
22-
use crate::message::{FluentAttribute, FluentMessage};
22+
use crate::message::FluentMessage;
2323
use crate::resolver::{ResolveValue, Scope, WriteValue};
2424
use crate::resource::FluentResource;
2525
use crate::types::FluentValue;
@@ -187,10 +187,9 @@ impl<R, M> FluentBundle<R, M> {
187187

188188
for (entry_pos, entry) in res.entries().enumerate() {
189189
let (id, entry) = match entry {
190-
ast::Entry::Message(ast::Message { ref id, .. }) => (
191-
id.name,
192-
Entry::Message((res_pos, entry_pos)),
193-
),
190+
ast::Entry::Message(ast::Message { ref id, .. }) => {
191+
(id.name, Entry::Message((res_pos, entry_pos)))
192+
}
194193
ast::Entry::Term(ast::Term { ref id, .. }) => {
195194
(id.name, Entry::Term((res_pos, entry_pos)))
196195
}
@@ -205,7 +204,7 @@ impl<R, M> FluentBundle<R, M> {
205204
let kind = match entry {
206205
Entry::Message(..) => EntryKind::Message,
207206
Entry::Term(..) => EntryKind::Term,
208-
_ => unreachable!()
207+
_ => unreachable!(),
209208
};
210209
errors.push(FluentError::Overriding {
211210
kind,
@@ -328,11 +327,7 @@ impl<R, M> FluentBundle<R, M> {
328327
/// and `fluent-pseudo` crate provides a function
329328
/// that can be passed here.
330329
pub fn set_transform(&mut self, func: Option<fn(&str) -> Cow<str>>) {
331-
if let Some(f) = func {
332-
self.transform = Some(f);
333-
} else {
334-
self.transform = None;
335-
}
330+
self.transform = func;
336331
}
337332

338333
/// This method allows to specify a function that will
@@ -342,11 +337,7 @@ impl<R, M> FluentBundle<R, M> {
342337
/// It's particularly useful for plugging in an external
343338
/// formatter for `FluentValue::Number`.
344339
pub fn set_formatter(&mut self, func: Option<fn(&FluentValue, &M) -> Option<String>>) {
345-
if let Some(f) = func {
346-
self.formatter = Some(f);
347-
} else {
348-
self.formatter = None;
349-
}
340+
self.formatter = func;
350341
}
351342

352343
/// Returns true if this bundle contains a message with the given id.
@@ -395,21 +386,11 @@ impl<R, M> FluentBundle<R, M> {
395386
/// let msg = bundle.get_message("hello-world");
396387
/// assert_eq!(msg.is_some(), true);
397388
/// ```
398-
pub fn get_message(&self, id: &str) -> Option<FluentMessage>
389+
pub fn get_message(&self, id: &str) -> Option<FluentMessage<&str>>
399390
where
400391
R: Borrow<FluentResource>,
401392
{
402-
let message = self.get_entry_message(id)?;
403-
let value = message.value.as_ref();
404-
let mut attributes = Vec::with_capacity(message.attributes.len());
405-
406-
for attr in &message.attributes {
407-
attributes.push(FluentAttribute {
408-
id: attr.id.name,
409-
value: &attr.value,
410-
});
411-
}
412-
Some(FluentMessage { value, attributes })
393+
self.get_entry_message(id).map(Into::into)
413394
}
414395

415396
/// Writes a formatted pattern which comes from a `FluentMessage`.

fluent-bundle/src/entry.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@ pub trait GetEntry {
2626

2727
impl<'bundle, R: Borrow<FluentResource>, M> GetEntry for FluentBundle<R, M> {
2828
fn get_entry_message(&self, id: &str) -> Option<&ast::Message<&str>> {
29-
self.entries.get(id).and_then(|entry| match *entry {
29+
self.entries.get(id).and_then(|ref entry| match entry {
3030
Entry::Message(pos) => {
3131
let res = self.resources.get(pos.0)?.borrow();
32-
if let Some(ast::Entry::Message(ref msg)) = res.get_entry(pos.1) {
32+
if let ast::Entry::Message(ref msg) = res.get_entry(pos.1)? {
3333
Some(msg)
3434
} else {
3535
None
@@ -40,10 +40,10 @@ impl<'bundle, R: Borrow<FluentResource>, M> GetEntry for FluentBundle<R, M> {
4040
}
4141

4242
fn get_entry_term(&self, id: &str) -> Option<&ast::Term<&str>> {
43-
self.entries.get(id).and_then(|entry| match *entry {
43+
self.entries.get(id).and_then(|ref entry| match entry {
4444
Entry::Term(pos) => {
4545
let res = self.resources.get(pos.0)?.borrow();
46-
if let Some(ast::Entry::Term(ref msg)) = res.get_entry(pos.1) {
46+
if let ast::Entry::Term(ref msg) = res.get_entry(pos.1)? {
4747
Some(msg)
4848
} else {
4949
None
@@ -54,7 +54,7 @@ impl<'bundle, R: Borrow<FluentResource>, M> GetEntry for FluentBundle<R, M> {
5454
}
5555

5656
fn get_entry_function(&self, id: &str) -> Option<&FluentFunction> {
57-
self.entries.get(id).and_then(|entry| match entry {
57+
self.entries.get(id).and_then(|ref entry| match entry {
5858
Entry::Function(function) => Some(function),
5959
_ => None,
6060
})

fluent-bundle/src/lib.rs

Lines changed: 69 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,81 @@
33
//! `fluent-bundle` is the mid-level component of the [Fluent Localization
44
//! System](https://www.projectfluent.org).
55
//!
6-
//! It builds on top of the low level [`fluent-syntax`](../fluent-syntax) package, and provides
6+
//! The crate builds on top of the low level [`fluent-syntax`](../fluent-syntax) package, and provides
77
//! foundational types and structures required for executing localization at runtime.
88
//!
9-
//! # Fluent Bundle
10-
//! The core structure on that level is [`FluentBundle`].
9+
//! There are four core concepts to understand Fluent runtime:
1110
//!
12-
//! [`FluentBundle`] is a low level container for storing and formatting localization messages
13-
//! in a single locale.
11+
//! # 1) Message
1412
//!
15-
//! This crate provides also a number of structures needed for a localization API such as [`FluentResource`],
16-
//! [`FluentMessage`], [`FluentArgs`], and [`FluentValue`].
13+
//! [`FluentMessage`] is the core unit of the system.
14+
//! It has an identifier, a value and a list of attributes.
1715
//!
18-
//! Together, they allow implementations to build higher-level APIs that use [`FluentBundle`]
19-
//! and add user friendly helpers, framework bindings, error fallbacking,
20-
//! language negotiation between user requested languages and available resources,
21-
//! and I/O for loading selected resources.
16+
//! The identifier is a key that must be unique within a [`FluentResource`] to
17+
//! which the message belongs to.
2218
//!
23-
//! # Example
19+
//! The shape of the message must also contain a value, attributes or both.
20+
//!
21+
//! ### Simple Message
22+
//!
23+
//! ```text
24+
//! hello-world = Hello, { $user }!
25+
//! ```
26+
//!
27+
//! ### Compound Message
28+
//!
29+
//! ```text
30+
//! confirm-modal = Are you sure?
31+
//! .confirm = Yes
32+
//! .cancel = No
33+
//! .tooltip = Closing the window will lose all unsaved data.
34+
//! ```
35+
//!
36+
//! # 2) Resource
37+
//!
38+
//! [`FluentResource`] wraps an [`Abstract Syntax Tree`](../fluent_syntax/ast/index.html) produced by the
39+
//! [`parser`](../fluent_syntax/parser/index.html) and provides an access to a list
40+
//! of its entries.
41+
//!
42+
//! A good mental model for a resource is a single FTL file, but in the future
43+
//! there's nothing preventing a resource from being stored in a data base,
44+
//! pre-parsed format or in some other structured form.
45+
//!
46+
//! # 3) Bundle
47+
//!
48+
//! [`FluentBundle`] is the core structure of the Fluent system at runtime.
49+
//!
50+
//! It is a single-locale container storing a combination of multiple [`FluentResource`]
51+
//! instances, combined with a set of internationalization components.
52+
//!
53+
//! The main function of the bundle is to provide a context in which messages can
54+
//! reference each other, terms, and functions, and use memoized internationalization
55+
//! components to provide resolved localization messages.
56+
//!
57+
//! A bundle is a thin wrapper, usually storing just references to the resources allocated
58+
//! in a long-lived collection, like a resource manager or a simple vector.
59+
//!
60+
//! In result, [`FluentBundle`] is cheap to construct, and higher level APIs can
61+
//! easily generate different permutations of [`FluentResource`] lists and
62+
//! resolve messages within those combinations.
63+
//!
64+
//! # 4) Arguments & Values
65+
//!
66+
//! [`FluentArgs`] is a collection, similar to a `HashMap`, which stores a key-value pair list of
67+
//! arguments provided by the developer to the [`format_pattern`](FluentBundle::format_pattern) method.
68+
//! Those arguments are used during message formatting to resolve selections, or can be
69+
//! interpolated into the message as a variable.
70+
//!
71+
//! The keys of the argument list are strings, and the values are limited to one of the
72+
//! [`FluentValue`] types.
73+
//!
74+
//! # Summary
75+
//!
76+
//! Together, [`FluentMessage`], [`FluentResource`], [`FluentBundle`], and [`FluentArgs`] provide
77+
//! all the necessary components of the Fluent localization system, and are sufficient
78+
//! to complete a simple localization API.
79+
//!
80+
//! ## Example
2481
//!
2582
//! ```
2683
//! use fluent_bundle::{FluentBundle, FluentValue, FluentResource, FluentArgs};

fluent-bundle/src/message.rs

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,41 @@
1-
use fluent_syntax::ast;
1+
use fluent_syntax::{ast, parser::Slice};
22

33
#[derive(Debug, PartialEq)]
4-
pub struct FluentAttribute<'m> {
5-
pub id: &'m str,
6-
pub value: &'m ast::Pattern<&'m str>,
4+
pub struct FluentAttribute<'m, S> {
5+
pub id: &'m S,
6+
pub value: &'m ast::Pattern<S>,
7+
}
8+
9+
impl<'m, S> From<&'m ast::Attribute<S>> for FluentAttribute<'m, S> {
10+
fn from(attr: &'m ast::Attribute<S>) -> Self {
11+
FluentAttribute {
12+
id: &attr.id.name,
13+
value: &attr.value,
14+
}
15+
}
716
}
817
/// A single localization unit composed of an identifier,
918
/// value, and attributes.
1019
#[derive(Debug, PartialEq)]
11-
pub struct FluentMessage<'m> {
12-
pub value: Option<&'m ast::Pattern<&'m str>>,
13-
pub attributes: Vec<FluentAttribute<'m>>,
20+
pub struct FluentMessage<'m, S> {
21+
pub value: Option<&'m ast::Pattern<S>>,
22+
pub attributes: Vec<FluentAttribute<'m, S>>,
23+
}
24+
25+
impl<'m, S> FluentMessage<'m, S> {
26+
pub fn get_attribute(&self, key: &str) -> Option<&FluentAttribute<S>>
27+
where
28+
S: Slice<'m>,
29+
{
30+
self.attributes.iter().find(|attr| attr.id.as_ref() == key)
31+
}
1432
}
1533

16-
impl<'m> FluentMessage<'m> {
17-
pub fn get_attribute(&self, key: &str) -> Option<&FluentAttribute> {
18-
self.attributes.iter().find(|attr| attr.id == key)
34+
impl<'m, S> From<&'m ast::Message<S>> for FluentMessage<'m, S> {
35+
fn from(msg: &'m ast::Message<S>) -> Self {
36+
FluentMessage {
37+
value: msg.value.as_ref(),
38+
attributes: msg.attributes.iter().map(Into::into).collect(),
39+
}
1940
}
2041
}

fluent-syntax/src/parser/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ mod slice;
7979

8080
use crate::ast;
8181
pub use errors::{ErrorKind, ParserError};
82-
use slice::Slice;
82+
pub use slice::Slice;
8383

8484
/// Parser result always returns an AST representation of the input,
8585
/// and if parsing errors were encountered, a list of [`ParserError`] elements

0 commit comments

Comments
 (0)