| 1 | use std::borrow::Cow; | 
| 2 | use std::iter::FromIterator; | 
|---|
| 3 |  | 
|---|
| 4 | use crate::types::FluentValue; | 
|---|
| 5 |  | 
|---|
| 6 | /// Fluent messages can use arguments in order to programmatically add values to a | 
|---|
| 7 | /// translated string. For instance, in a localized application you may wish to display | 
|---|
| 8 | /// a user's email count. This could be done with the following message. | 
|---|
| 9 | /// | 
|---|
| 10 | /// `msg-key = Hello, { $user }. You have { $emailCount } messages.` | 
|---|
| 11 | /// | 
|---|
| 12 | /// Here `$user` and `$emailCount` are the arguments, which can be filled with values. | 
|---|
| 13 | /// | 
|---|
| 14 | /// The [`FluentArgs`] struct is the map from the argument name (for example `$user`) to | 
|---|
| 15 | /// the argument value (for example "John".) The logic to apply these to write these | 
|---|
| 16 | /// to messages is elsewhere, this struct just stores the value. | 
|---|
| 17 | /// | 
|---|
| 18 | /// # Example | 
|---|
| 19 | /// | 
|---|
| 20 | /// ``` | 
|---|
| 21 | /// use fluent_bundle::{FluentArgs, FluentBundle, FluentResource}; | 
|---|
| 22 | /// | 
|---|
| 23 | /// let mut args = FluentArgs::new(); | 
|---|
| 24 | /// args.set( "user", "John"); | 
|---|
| 25 | /// args.set( "emailCount", 5); | 
|---|
| 26 | /// | 
|---|
| 27 | /// let res = FluentResource::try_new( r#" | 
|---|
| 28 | /// | 
|---|
| 29 | /// msg-key = Hello, { $user }. You have { $emailCount } messages. | 
|---|
| 30 | /// | 
|---|
| 31 | /// "#.to_string()) | 
|---|
| 32 | ///     .expect( "Failed to parse FTL."); | 
|---|
| 33 | /// | 
|---|
| 34 | /// let mut bundle = FluentBundle::default(); | 
|---|
| 35 | /// | 
|---|
| 36 | /// // For this example, we'll turn on BiDi support. | 
|---|
| 37 | /// // Please, be careful when doing it, it's a risky move. | 
|---|
| 38 | /// bundle.set_use_isolating(false); | 
|---|
| 39 | /// | 
|---|
| 40 | /// bundle.add_resource(res) | 
|---|
| 41 | ///     .expect( "Failed to add a resource."); | 
|---|
| 42 | /// | 
|---|
| 43 | /// let mut err = vec![]; | 
|---|
| 44 | /// | 
|---|
| 45 | /// let msg = bundle.get_message( "msg-key") | 
|---|
| 46 | ///     .expect( "Failed to retrieve a message."); | 
|---|
| 47 | /// let value = msg.value() | 
|---|
| 48 | ///     .expect( "Failed to retrieve a value."); | 
|---|
| 49 | /// | 
|---|
| 50 | /// assert_eq!( | 
|---|
| 51 | ///     bundle.format_pattern(value, Some(&args), &mut err), | 
|---|
| 52 | /// "Hello, John. You have 5 messages." | 
|---|
| 53 | /// ); | 
|---|
| 54 | /// ``` | 
|---|
| 55 | #[ derive(Debug, Default)] | 
|---|
| 56 | pub struct FluentArgs<'args>(Vec<(Cow<'args, str>, FluentValue<'args>)>); | 
|---|
| 57 |  | 
|---|
| 58 | impl<'args> FluentArgs<'args> { | 
|---|
| 59 | /// Creates a new empty argument map. | 
|---|
| 60 | pub fn new() -> Self { | 
|---|
| 61 | Self::default() | 
|---|
| 62 | } | 
|---|
| 63 |  | 
|---|
| 64 | /// Pre-allocates capacity for arguments. | 
|---|
| 65 | pub fn with_capacity(capacity: usize) -> Self { | 
|---|
| 66 | Self(Vec::with_capacity(capacity)) | 
|---|
| 67 | } | 
|---|
| 68 |  | 
|---|
| 69 | /// Gets the [`FluentValue`] at the `key` if it exists. | 
|---|
| 70 | pub fn get<K>(&self, key: K) -> Option<&FluentValue<'args>> | 
|---|
| 71 | where | 
|---|
| 72 | K: Into<Cow<'args, str>>, | 
|---|
| 73 | { | 
|---|
| 74 | let key = key.into(); | 
|---|
| 75 | if let Ok(idx) = self.0.binary_search_by_key(&&key, |(k, _)| k) { | 
|---|
| 76 | Some(&self.0[idx].1) | 
|---|
| 77 | } else { | 
|---|
| 78 | None | 
|---|
| 79 | } | 
|---|
| 80 | } | 
|---|
| 81 |  | 
|---|
| 82 | /// Sets the key value pair. | 
|---|
| 83 | pub fn set<K, V>(&mut self, key: K, value: V) | 
|---|
| 84 | where | 
|---|
| 85 | K: Into<Cow<'args, str>>, | 
|---|
| 86 | V: Into<FluentValue<'args>>, | 
|---|
| 87 | { | 
|---|
| 88 | let key = key.into(); | 
|---|
| 89 | match self.0.binary_search_by_key(&&key, |(k, _)| k) { | 
|---|
| 90 | Ok(idx) => self.0[idx] = (key, value.into()), | 
|---|
| 91 | Err(idx) => self.0.insert(idx, (key, value.into())), | 
|---|
| 92 | }; | 
|---|
| 93 | } | 
|---|
| 94 |  | 
|---|
| 95 | /// Iterate over a tuple of the key an [`FluentValue`]. | 
|---|
| 96 | pub fn iter(&self) -> impl Iterator<Item = (&str, &FluentValue)> { | 
|---|
| 97 | self.0.iter().map(|(k, v)| (k.as_ref(), v)) | 
|---|
| 98 | } | 
|---|
| 99 | } | 
|---|
| 100 |  | 
|---|
| 101 | impl<'args, K, V> FromIterator<(K, V)> for FluentArgs<'args> | 
|---|
| 102 | where | 
|---|
| 103 | K: Into<Cow<'args, str>>, | 
|---|
| 104 | V: Into<FluentValue<'args>>, | 
|---|
| 105 | { | 
|---|
| 106 | fn from_iter<I>(iter: I) -> Self | 
|---|
| 107 | where | 
|---|
| 108 | I: IntoIterator<Item = (K, V)>, | 
|---|
| 109 | { | 
|---|
| 110 | let iter: ::IntoIter = iter.into_iter(); | 
|---|
| 111 | let mut args: FluentArgs<'_> = if let Some(size: usize) = iter.size_hint().1 { | 
|---|
| 112 | FluentArgs::with_capacity(size) | 
|---|
| 113 | } else { | 
|---|
| 114 | FluentArgs::new() | 
|---|
| 115 | }; | 
|---|
| 116 |  | 
|---|
| 117 | for (k: K, v: V) in iter { | 
|---|
| 118 | args.set(key:k, value:v); | 
|---|
| 119 | } | 
|---|
| 120 |  | 
|---|
| 121 | args | 
|---|
| 122 | } | 
|---|
| 123 | } | 
|---|
| 124 |  | 
|---|
| 125 | impl<'args> IntoIterator for FluentArgs<'args> { | 
|---|
| 126 | type Item = (Cow<'args, str>, FluentValue<'args>); | 
|---|
| 127 | type IntoIter = std::vec::IntoIter<Self::Item>; | 
|---|
| 128 |  | 
|---|
| 129 | fn into_iter(self) -> Self::IntoIter { | 
|---|
| 130 | self.0.into_iter() | 
|---|
| 131 | } | 
|---|
| 132 | } | 
|---|
| 133 |  | 
|---|
| 134 | #[ cfg(test)] | 
|---|
| 135 | mod tests { | 
|---|
| 136 | use super::*; | 
|---|
| 137 |  | 
|---|
| 138 | #[ test] | 
|---|
| 139 | fn replace_existing_arguments() { | 
|---|
| 140 | let mut args = FluentArgs::new(); | 
|---|
| 141 |  | 
|---|
| 142 | args.set( "name", "John"); | 
|---|
| 143 | args.set( "emailCount", 5); | 
|---|
| 144 | assert_eq!(args.0.len(), 2); | 
|---|
| 145 | assert_eq!( | 
|---|
| 146 | args.get( "name"), | 
|---|
| 147 | Some(&FluentValue::String(Cow::Borrowed( "John"))) | 
|---|
| 148 | ); | 
|---|
| 149 | assert_eq!(args.get( "emailCount"), Some(&FluentValue::try_number( "5"))); | 
|---|
| 150 |  | 
|---|
| 151 | args.set( "name", "Jane"); | 
|---|
| 152 | args.set( "emailCount", 7); | 
|---|
| 153 | assert_eq!(args.0.len(), 2); | 
|---|
| 154 | assert_eq!( | 
|---|
| 155 | args.get( "name"), | 
|---|
| 156 | Some(&FluentValue::String(Cow::Borrowed( "Jane"))) | 
|---|
| 157 | ); | 
|---|
| 158 | assert_eq!(args.get( "emailCount"), Some(&FluentValue::try_number( "7"))); | 
|---|
| 159 | } | 
|---|
| 160 | } | 
|---|
| 161 |  | 
|---|