Skip to content

Commit 5f10ca9

Browse files
committed
Refactor fluent_args
1 parent 239f5a2 commit 5f10ca9

File tree

13 files changed

+105
-90
lines changed

13 files changed

+105
-90
lines changed

fluent-bundle/benches/resolver.rs

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::fs::File;
66
use std::io;
77
use std::io::Read;
88

9-
use fluent_bundle::{FluentBundle, FluentResource, FluentValue};
9+
use fluent_bundle::{fluent_args, FluentArgs, FluentBundle, FluentResource};
1010
use fluent_syntax::ast;
1111

1212
fn read_file(path: &str) -> Result<String, io::Error> {
@@ -38,23 +38,21 @@ fn get_ids(res: &FluentResource) -> Vec<String> {
3838
.collect()
3939
}
4040

41-
fn get_args(name: &str) -> Option<HashMap<String, FluentValue>> {
41+
fn get_args(name: &str) -> Option<FluentArgs> {
4242
match name {
4343
"preferences" => {
44-
let mut prefs_args = HashMap::new();
45-
prefs_args.insert("name".to_string(), FluentValue::from("John"));
46-
prefs_args.insert("tabCount".to_string(), FluentValue::from(5));
47-
prefs_args.insert("count".to_string(), FluentValue::from(3));
48-
prefs_args.insert("version".to_string(), FluentValue::from("65.0"));
49-
prefs_args.insert("path".to_string(), FluentValue::from("/tmp"));
50-
prefs_args.insert("num".to_string(), FluentValue::from(4));
51-
prefs_args.insert("email".to_string(), FluentValue::from("[email protected]"));
52-
prefs_args.insert("value".to_string(), FluentValue::from(4.5));
53-
prefs_args.insert("unit".to_string(), FluentValue::from("mb"));
54-
prefs_args.insert(
55-
"service-name".to_string(),
56-
FluentValue::from("Mozilla Disk"),
57-
);
44+
let prefs_args = fluent_args![
45+
"name" => "John",
46+
"tabCount" => 5,
47+
"count" => 3,
48+
"version" => "65.0",
49+
"path" => "/tmp",
50+
"num" => 4,
51+
"email" => "[email protected]",
52+
"value" => 4.5,
53+
"unit" => "mb",
54+
"service-name" => "Mozilla Disk"
55+
];
5856
Some(prefs_args)
5957
}
6058
_ => None,

fluent-bundle/examples/external_arguments.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
use fluent_bundle::{FluentBundle, FluentResource, FluentValue};
2-
use std::collections::HashMap;
1+
use fluent_bundle::{fluent_args, FluentBundle, FluentResource, FluentValue};
32
use unic_langid::langid;
43

54
fn main() {
@@ -21,8 +20,9 @@ unread-emails =
2120
.add_resource(res)
2221
.expect("Failed to add FTL resources to the bundle.");
2322

24-
let mut args = HashMap::new();
25-
args.insert("name".to_string(), FluentValue::from("John"));
23+
let args = fluent_args![
24+
"name" => "John"
25+
];
2626

2727
let msg = bundle
2828
.get_message("hello-world")
@@ -38,8 +38,9 @@ unread-emails =
3838
let value = bundle.format_pattern(&pattern, Some(&args), &mut errors);
3939
println!("{}", value);
4040

41-
let mut args = HashMap::new();
42-
args.insert("emailCount".to_string(), FluentValue::into_number("1.0"));
41+
let args = fluent_args![
42+
"emailCount" => FluentValue::into_number("1.0")
43+
];
4344

4445
let msg = bundle
4546
.get_message("unread-emails")

fluent-bundle/examples/selector.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
use fluent_bundle::{FluentBundle, FluentResource, FluentValue};
2-
use std::collections::HashMap;
1+
use fluent_bundle::{fluent_args, FluentBundle, FluentResource};
32

43
fn main() {
54
let ftl_string = String::from(
@@ -29,8 +28,9 @@ hello-world2 = Hello { $name ->
2928
let value = bundle.format_pattern(&pattern, None, &mut errors);
3029
println!("{}", value);
3130

32-
let mut args = HashMap::new();
33-
args.insert("name".to_string(), FluentValue::from("moon"));
31+
let args = fluent_args![
32+
"name" => "moon"
33+
];
3434

3535
let msg = bundle
3636
.get_message("hello-world2")

fluent-bundle/examples/simple-app.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,8 @@
1717
//!
1818
//! If the second argument is omitted, `en-US` locale is used as the
1919
//! default one.
20-
use fluent_bundle::{FluentBundle, FluentResource, FluentValue};
20+
use fluent_bundle::{fluent_args, FluentBundle, FluentResource};
2121
use fluent_locale::{negotiate_languages, NegotiationStrategy};
22-
use std::collections::HashMap;
2322
use std::env;
2423
use std::fs;
2524
use std::fs::File;
@@ -130,9 +129,10 @@ fn main() {
130129
Ok(i) => {
131130
// 7.2. Construct a map of arguments
132131
// to format the message.
133-
let mut args = HashMap::new();
134-
args.insert("input".to_string(), FluentValue::from(i));
135-
args.insert("value".to_string(), FluentValue::from(collatz(i)));
132+
let args = fluent_args![
133+
"input" => i,
134+
"value" => collatz(i)
135+
];
136136
// 7.3. Format the message.
137137
let mut errors = vec![];
138138
let msg = bundle
@@ -143,9 +143,10 @@ fn main() {
143143
println!("{}", value);
144144
}
145145
Err(err) => {
146-
let mut args = HashMap::new();
147-
args.insert("input".to_string(), FluentValue::from(input.as_str()));
148-
args.insert("reason".to_string(), FluentValue::from(err.to_string()));
146+
let args = fluent_args![
147+
"input" => input.as_str(),
148+
"reason" => err.to_string()
149+
];
149150
let mut errors = vec![];
150151
let msg = bundle
151152
.get_message("input-parse-error")

fluent-bundle/src/bundle.rs

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,28 @@ pub struct FluentMessage<'m> {
2828
pub attributes: HashMap<&'m str, &'m ast::Pattern<'m>>,
2929
}
3030

31+
pub type FluentArgs<'args> = HashMap<&'args str, FluentValue<'args>>;
32+
33+
#[macro_export]
34+
macro_rules! fluent_args {
35+
( $($key:expr => $value:expr),* ) => {
36+
{
37+
let mut args: ::fluent_bundle::bundle::FluentArgs = std::collections::HashMap::new();
38+
$(
39+
args.insert($key, $value.into());
40+
)*
41+
args
42+
}
43+
};
44+
}
45+
3146
/// A collection of localization messages for a single locale, which are meant
3247
/// to be used together in a single view, widget or any other UI abstraction.
3348
///
3449
/// # Examples
3550
///
3651
/// ```
37-
/// use fluent_bundle::{FluentBundle, FluentResource, FluentValue};
52+
/// use fluent_bundle::{FluentBundle, FluentResource, FluentValue, fluent_args};
3853
/// use std::collections::HashMap;
3954
/// use unic_langid::langid;
4055
///
@@ -47,8 +62,9 @@ pub struct FluentMessage<'m> {
4762
/// bundle.add_resource(&resource)
4863
/// .expect("Failed to add FTL resources to the bundle.");
4964
///
50-
/// let mut args = HashMap::new();
51-
/// args.insert("name".to_string(), FluentValue::from("Rustacean"));
65+
/// let args = fluent_args![
66+
/// "name" => "Rustacean"
67+
/// ];
5268
///
5369
/// let msg = bundle.get_message("intro").expect("Message doesn't exist.");
5470
/// let mut errors = vec![];
@@ -282,7 +298,7 @@ impl<R> FluentBundle<R> {
282298
pub fn format_pattern<'bundle>(
283299
&'bundle self,
284300
pattern: &'bundle ast::Pattern,
285-
args: Option<&'bundle HashMap<String, FluentValue>>,
301+
args: Option<&'bundle FluentArgs>,
286302
errors: &mut Vec<FluentError>,
287303
) -> Cow<'bundle, str>
288304
where
@@ -335,9 +351,7 @@ impl<R> FluentBundle<R> {
335351
/// [FTL syntax guide]: https://projectfluent.org/fluent/guide/functions.html
336352
pub fn add_function<F: 'static>(&mut self, id: &str, func: F) -> Result<(), FluentError>
337353
where
338-
F: for<'a> Fn(&[FluentValue<'a>], &HashMap<String, FluentValue<'a>>) -> FluentValue<'a>
339-
+ Sync
340-
+ Send,
354+
F: for<'a> Fn(&[FluentValue<'a>], &FluentArgs) -> FluentValue<'a> + Sync + Send,
341355
{
342356
match self.entries.entry(id.to_owned()) {
343357
HashEntry::Vacant(entry) => {

fluent-bundle/src/entry.rs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,15 @@
11
//! `Entry` is used to store Messages, Terms and Functions in `FluentBundle` instances.
22
33
use std::borrow::Borrow;
4-
use std::collections::HashMap;
54

65
use fluent_syntax::ast;
76

8-
use crate::bundle::FluentBundle;
7+
use crate::bundle::{FluentArgs, FluentBundle};
98
use crate::resource::FluentResource;
109
use crate::types::FluentValue;
1110

12-
pub type FluentFunction = Box<
13-
dyn for<'a> Fn(&[FluentValue<'a>], &HashMap<String, FluentValue<'a>>) -> FluentValue<'a>
14-
+ Send
15-
+ Sync,
16-
>;
11+
pub type FluentFunction =
12+
Box<dyn for<'a> Fn(&[FluentValue<'a>], &FluentArgs) -> FluentValue<'a> + Send + Sync>;
1713

1814
pub enum Entry {
1915
Message([usize; 2]),

fluent-bundle/src/lib.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@
1111
//! # Example
1212
//!
1313
//! ```
14-
//! use fluent_bundle::{FluentBundle, FluentValue, FluentResource};
15-
//! use std::collections::HashMap;
14+
//! use fluent_bundle::{FluentBundle, FluentResource, fluent_args};
1615
//! use unic_langid::langid;
1716
//!
1817
//! let ftl_string = String::from("
@@ -36,8 +35,9 @@
3635
//!
3736
//! assert_eq!(&value, "Hello, world!");
3837
//!
39-
//! let mut args = HashMap::new();
40-
//! args.insert("name".to_string(), FluentValue::from("John"));
38+
//! let args = fluent_args![
39+
//! "name" => "John"
40+
//! ];
4141
//!
4242
//! let msg = bundle.get_message("intro").expect("Message doesn't exist.");
4343
//! let mut errors = vec![];
@@ -59,6 +59,6 @@ pub mod resolve;
5959
pub mod resource;
6060
pub mod types;
6161

62-
pub use bundle::FluentBundle;
62+
pub use bundle::{FluentArgs, FluentBundle};
6363
pub use resource::FluentResource;
6464
pub use types::FluentValue;

fluent-bundle/src/resolve.rs

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,12 @@
77
//! [`FluentBundle`]: ../bundle/struct.FluentBundle.html
88
99
use std::borrow::Borrow;
10-
use std::collections::HashMap;
1110
use std::fmt::Write;
1211

1312
use fluent_syntax::ast;
1413
use fluent_syntax::unicode::unescape_unicode;
1514

16-
use crate::bundle::FluentBundle;
15+
use crate::bundle::{FluentArgs, FluentBundle};
1716
use crate::entry::GetEntry;
1817
use crate::resource::FluentResource;
1918
use crate::types::DisplayableNode;
@@ -31,20 +30,17 @@ pub struct Scope<'bundle, R: Borrow<FluentResource>> {
3130
/// The current `FluentBundle` instance.
3231
pub bundle: &'bundle FluentBundle<R>,
3332
/// The current arguments passed by the developer.
34-
args: Option<&'bundle HashMap<String, FluentValue<'bundle>>>,
33+
args: Option<&'bundle FluentArgs<'bundle>>,
3534
/// Local args
36-
local_args: Option<HashMap<String, FluentValue<'bundle>>>,
35+
local_args: Option<FluentArgs<'bundle>>,
3736
/// Tracks hashes to prevent infinite recursion.
3837
travelled: smallvec::SmallVec<[&'bundle ast::Pattern<'bundle>; 2]>,
3938
/// Track errors accumulated during resolving.
4039
pub errors: Vec<ResolverError>,
4140
}
4241

4342
impl<'bundle, R: Borrow<FluentResource>> Scope<'bundle, R> {
44-
pub fn new(
45-
bundle: &'bundle FluentBundle<R>,
46-
args: Option<&'bundle HashMap<String, FluentValue>>,
47-
) -> Self {
43+
pub fn new(bundle: &'bundle FluentBundle<R>, args: Option<&'bundle FluentArgs>) -> Self {
4844
Scope {
4945
bundle,
5046
args,
@@ -281,23 +277,20 @@ impl<'source> ResolveValue<'source> for ast::InlineExpression<'source> {
281277
fn get_arguments<'bundle, R>(
282278
scope: &mut Scope<'bundle, R>,
283279
arguments: &'bundle Option<ast::CallArguments<'bundle>>,
284-
) -> (
285-
Vec<FluentValue<'bundle>>,
286-
HashMap<String, FluentValue<'bundle>>,
287-
)
280+
) -> (Vec<FluentValue<'bundle>>, FluentArgs<'bundle>)
288281
where
289282
R: Borrow<FluentResource>,
290283
{
291284
let mut resolved_positional_args = Vec::new();
292-
let mut resolved_named_args = HashMap::new();
285+
let mut resolved_named_args = FluentArgs::new();
293286

294287
if let Some(ast::CallArguments { named, positional }) = arguments {
295288
for expression in positional {
296289
resolved_positional_args.push(expression.resolve(scope));
297290
}
298291

299292
for arg in named {
300-
resolved_named_args.insert(arg.name.name.to_string(), arg.value.resolve(scope));
293+
resolved_named_args.insert(arg.name.name, arg.value.resolve(scope));
301294
}
302295
}
303296

fluent-bundle/tests/resolver_fixtures.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use std::iter;
66
use std::path::Path;
77
use std::str::FromStr;
88

9+
use fluent_bundle::bundle::FluentArgs;
910
use fluent_bundle::errors::FluentError;
1011
use fluent_bundle::resolve::ResolverError;
1112
use fluent_bundle::{FluentBundle as FluentBundleGeneric, FluentResource, FluentValue};
@@ -264,14 +265,14 @@ fn test_test(test: &Test, defaults: &Option<TestDefaults>, mut scope: Scope) {
264265
))
265266
};
266267

267-
let args: Option<HashMap<String, FluentValue>> = assert.args.as_ref().map(|args| {
268+
let args: Option<FluentArgs> = assert.args.as_ref().map(|args| {
268269
args.iter()
269270
.map(|(k, v)| {
270271
let val = match f64::from_str(v) {
271272
Ok(_) => FluentValue::Number(v.into()),
272273
Err(_) => FluentValue::String(v.into()),
273274
};
274-
(k.to_string(), val)
275+
(k.as_str(), val)
275276
})
276277
.collect()
277278
});

fluent-bundle/tests/types_test.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use fluent_bundle::fluent_args;
12
use fluent_bundle::resolve::Scope;
23
use fluent_bundle::types::FluentValue;
34
use fluent_bundle::FluentBundle;
@@ -74,3 +75,14 @@ fn fluent_value_from() {
7475
assert_eq!(value_f64, FluentValue::Number("23.5".into()));
7576
assert_eq!(value_isize, FluentValue::Number("-23".into()));
7677
}
78+
79+
#[test]
80+
fn fluent_args() {
81+
let args = fluent_args![
82+
"name" => "John",
83+
"name2" => String::from("John2"),
84+
"emailCount" => 5,
85+
"foo" => FluentValue::into_number(5)
86+
];
87+
assert_eq!(args.get("name"), Some(&FluentValue::from("John")));
88+
}

fluent-fallback/examples/simple-fallback.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,9 @@
1818
//! If the second argument is omitted, `en-US` locale is used as the
1919
//! default one.
2020
use elsa::FrozenMap;
21-
use fluent_bundle::{FluentBundle, FluentResource, FluentValue};
21+
use fluent_bundle::{fluent_args, FluentBundle, FluentResource};
2222
use fluent_fallback::Localization;
2323
use fluent_locale::{negotiate_languages, NegotiationStrategy};
24-
use std::collections::HashMap;
2524
use std::env;
2625
use std::fs;
2726
use std::fs::File;
@@ -139,17 +138,19 @@ fn main() {
139138
Ok(i) => {
140139
// 7.2. Construct a map of arguments
141140
// to format the message.
142-
let mut args = HashMap::new();
143-
args.insert("input".to_string(), FluentValue::from(i));
144-
args.insert("value".to_string(), FluentValue::from(collatz(i)));
141+
let args = fluent_args![
142+
"input" => i,
143+
"value" => collatz(i)
144+
];
145145
// 7.3. Format the message.
146146
let value = loc.format_value("response-msg", Some(&args));
147147
println!("{}", value);
148148
}
149149
Err(err) => {
150-
let mut args = HashMap::new();
151-
args.insert("input".to_string(), FluentValue::from(input.as_str()));
152-
args.insert("reason".to_string(), FluentValue::from(err.to_string()));
150+
let args = fluent_args![
151+
"input" => input.as_str(),
152+
"reason" => err.to_string()
153+
];
153154
let value = loc.format_value("input-parse-error-msg", Some(&args));
154155
println!("{}", value);
155156
}

0 commit comments

Comments
 (0)