@@ -28,9 +28,19 @@ use crate::memoizer::MemoizerKind;
2828use crate :: resolver:: Scope ;
2929use crate :: resource:: FluentResource ;
3030
31+ /// Custom types can implement the [`FluentType`] trait in order to generate a string
32+ /// value for use in the message generation process.
3133pub trait FluentType : fmt:: Debug + AnyEq + ' static {
34+ /// Create a clone of the underlying type.
3235 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".
3339 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).
3444 fn as_string_threadsafe (
3545 & self ,
3646 intls : & intl_memoizer:: concurrent:: IntlLangMemoizer ,
@@ -101,15 +111,72 @@ impl<'s> Clone for FluentValue<'s> {
101111}
102112
103113impl < ' 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 ( )
108149 } else {
109- s . into ( )
150+ string . into ( )
110151 }
111152 }
112153
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+ /// ```
113180 pub fn matches < R : Borrow < FluentResource > , M > (
114181 & self ,
115182 other : & FluentValue ,
@@ -131,6 +198,8 @@ impl<'source> FluentValue<'source> {
131198 "other" => PluralCategory :: OTHER ,
132199 _ => return false ,
133200 } ;
201+ // This string matches a plural rule keyword. Check if the number
202+ // matches the plural rule category.
134203 scope
135204 . bundle
136205 . intls
@@ -144,6 +213,7 @@ impl<'source> FluentValue<'source> {
144213 }
145214 }
146215
216+ /// Write out a string version of the [`FluentValue`] to `W`.
147217 pub fn write < W , R , M > ( & self , w : & mut W , scope : & Scope < R , M > ) -> fmt:: Result
148218 where
149219 W : fmt:: Write ,
@@ -164,6 +234,7 @@ impl<'source> FluentValue<'source> {
164234 }
165235 }
166236
237+ /// Converts the [`FluentValue`] to a string.
167238 pub fn as_string < R : Borrow < FluentResource > , M > ( & self , scope : & Scope < R , M > ) -> Cow < ' source , str >
168239 where
169240 M : MemoizerKind ,
0 commit comments