@@ -28,9 +28,19 @@ use crate::memoizer::MemoizerKind;
28
28
use crate :: resolver:: Scope ;
29
29
use crate :: resource:: FluentResource ;
30
30
31
+ /// Custom types can implement the [`FluentType`] trait in order to generate a string
32
+ /// value for use in the message generation process.
31
33
pub trait FluentType : fmt:: Debug + AnyEq + ' static {
34
+ /// Create a clone of the underlying type.
32
35
fn duplicate ( & self ) -> Box < dyn FluentType + Send > ;
36
+
37
+ /// Convert the custom type into a string value, for instance a custom DateTime
38
+ /// type could return "Oct. 27, 2022".
33
39
fn as_string ( & self , intls : & intl_memoizer:: IntlLangMemoizer ) -> Cow < ' static , str > ;
40
+
41
+ /// Convert the custom type into a string value, for instance a custom DateTime
42
+ /// type could return "Oct. 27, 2022". This operation is provided the threadsafe
43
+ /// [IntlLangMemoizer](intl_memoizer::concurrent::IntlLangMemoizer).
34
44
fn as_string_threadsafe (
35
45
& self ,
36
46
intls : & intl_memoizer:: concurrent:: IntlLangMemoizer ,
@@ -101,15 +111,72 @@ impl<'s> Clone for FluentValue<'s> {
101
111
}
102
112
103
113
impl < ' source > FluentValue < ' source > {
104
- pub fn try_number < S : ToString > ( v : S ) -> Self {
105
- let s = v. to_string ( ) ;
106
- if let Ok ( num) = FluentNumber :: from_str ( & s) {
107
- num. into ( )
114
+ /// Attempts to parse the string representation of a `value` that supports
115
+ /// [`ToString`] into a [`FluentValue::Number`]. If it fails, it will instead
116
+ /// convert it to a [`FluentValue::String`].
117
+ ///
118
+ /// ```
119
+ /// use fluent_bundle::types::{FluentNumber, FluentNumberOptions, FluentValue};
120
+ ///
121
+ /// // "2" parses into a `FluentNumber`
122
+ /// assert_eq!(
123
+ /// FluentValue::try_number("2"),
124
+ /// FluentValue::Number(FluentNumber::new(2.0, FluentNumberOptions::default()))
125
+ /// );
126
+ ///
127
+ /// // Floats can be parsed as well.
128
+ /// assert_eq!(
129
+ /// FluentValue::try_number("3.141569"),
130
+ /// FluentValue::Number(FluentNumber::new(
131
+ /// 3.141569,
132
+ /// FluentNumberOptions {
133
+ /// minimum_fraction_digits: Some(6),
134
+ /// ..Default::default()
135
+ /// }
136
+ /// ))
137
+ /// );
138
+ ///
139
+ /// // When a value is not a valid number, it falls back to a `FluentValue::String`
140
+ /// assert_eq!(
141
+ /// FluentValue::try_number("A string"),
142
+ /// FluentValue::String("A string".into())
143
+ /// );
144
+ /// ```
145
+ pub fn try_number < S : ToString > ( value : S ) -> Self {
146
+ let string = value. to_string ( ) ;
147
+ if let Ok ( number) = FluentNumber :: from_str ( & string) {
148
+ number. into ( )
108
149
} else {
109
- s . into ( )
150
+ string . into ( )
110
151
}
111
152
}
112
153
154
+ /// Checks to see if two [`FluentValues`](FluentValue) match each other by having the
155
+ /// same type and contents. The special exception is in the case of a string being
156
+ /// compared to a number. Here attempt to check that the plural rule category matches.
157
+ ///
158
+ /// ```
159
+ /// use fluent_bundle::resolver::Scope;
160
+ /// use fluent_bundle::{types::FluentValue, FluentBundle, FluentResource};
161
+ /// use unic_langid::langid;
162
+ ///
163
+ /// let langid_ars = langid!("en");
164
+ /// let bundle: FluentBundle<FluentResource> = FluentBundle::new(vec![langid_ars]);
165
+ /// let scope = Scope::new(&bundle, None, None);
166
+ ///
167
+ /// // Matching examples:
168
+ /// assert!(FluentValue::try_number("2").matches(&FluentValue::try_number("2"), &scope));
169
+ /// assert!(FluentValue::from("fluent").matches(&FluentValue::from("fluent"), &scope));
170
+ /// assert!(
171
+ /// FluentValue::from("one").matches(&FluentValue::try_number("1"), &scope),
172
+ /// "Plural rules are matched."
173
+ /// );
174
+ ///
175
+ /// // Non-matching examples:
176
+ /// assert!(!FluentValue::try_number("2").matches(&FluentValue::try_number("3"), &scope));
177
+ /// assert!(!FluentValue::from("fluent").matches(&FluentValue::from("not fluent"), &scope));
178
+ /// assert!(!FluentValue::from("two").matches(&FluentValue::try_number("100"), &scope),);
179
+ /// ```
113
180
pub fn matches < R : Borrow < FluentResource > , M > (
114
181
& self ,
115
182
other : & FluentValue ,
@@ -131,6 +198,8 @@ impl<'source> FluentValue<'source> {
131
198
"other" => PluralCategory :: OTHER ,
132
199
_ => return false ,
133
200
} ;
201
+ // This string matches a plural rule keyword. Check if the number
202
+ // matches the plural rule category.
134
203
scope
135
204
. bundle
136
205
. intls
@@ -144,6 +213,7 @@ impl<'source> FluentValue<'source> {
144
213
}
145
214
}
146
215
216
+ /// Write out a string version of the [`FluentValue`] to `W`.
147
217
pub fn write < W , R , M > ( & self , w : & mut W , scope : & Scope < R , M > ) -> fmt:: Result
148
218
where
149
219
W : fmt:: Write ,
@@ -164,6 +234,7 @@ impl<'source> FluentValue<'source> {
164
234
}
165
235
}
166
236
237
+ /// Converts the [`FluentValue`] to a string.
167
238
pub fn as_string < R : Borrow < FluentResource > , M > ( & self , scope : & Scope < R , M > ) -> Cow < ' source , str >
168
239
where
169
240
M : MemoizerKind ,
0 commit comments