Skip to content

Commit 440f1c3

Browse files
committed
Add general documentation to fluent-bundle
1 parent 9fac18a commit 440f1c3

File tree

3 files changed

+102
-6
lines changed

3 files changed

+102
-6
lines changed

fluent-bundle/src/memoizer.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,29 @@ use crate::types::FluentType;
22
use intl_memoizer::Memoizable;
33
use unic_langid::LanguageIdentifier;
44

5+
/// This trait contains thread-safe methods which extend [intl_memoizer::IntlLangMemoizer].
6+
/// It is used as the generic bound in this crate when a memoizer is needed.
57
pub trait MemoizerKind: 'static {
68
fn new(lang: LanguageIdentifier) -> Self
79
where
810
Self: Sized;
911

10-
fn with_try_get_threadsafe<I, R, U>(&self, args: I::Args, cb: U) -> Result<R, I::Error>
12+
/// A threadsafe variant of `with_try_get` from [intl_memoizer::IntlLangMemoizer].
13+
/// The generics enforce that `Self` and its arguments are actually threadsafe.
14+
///
15+
/// `I` - The [Memoizable](intl_memoizer::Memoizable) internationalization formatter.
16+
///
17+
/// `R` - The result from the format operation.
18+
///
19+
/// `U` - The callback that accepts the instance of the intl formatter, and generates
20+
/// some kind of results `R`.
21+
fn with_try_get_threadsafe<I, R, U>(&self, args: I::Args, callback: U) -> Result<R, I::Error>
1122
where
1223
Self: Sized,
1324
I: Memoizable + Send + Sync + 'static,
1425
I::Args: Send + Sync + 'static,
1526
U: FnOnce(&I) -> R;
1627

28+
/// Wires up the `as_string` or `as_string_threadsafe` variants for [`FluentType`].
1729
fn stringify_value(&self, value: &dyn FluentType) -> std::borrow::Cow<'static, str>;
1830
}

fluent-bundle/src/types/mod.rs

Lines changed: 76 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,19 @@ use crate::memoizer::MemoizerKind;
2828
use crate::resolver::Scope;
2929
use 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.
3133
pub 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

103113
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()
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,

fluent-syntax/src/ast/mod.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1438,9 +1438,22 @@ pub enum InlineExpression<S> {
14381438
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
14391439
#[cfg_attr(feature = "serde", serde(untagged))]
14401440
pub enum Expression<S> {
1441+
/// A select expression such as:
1442+
/// ```ftl
1443+
/// key = { $var ->
1444+
/// [key1] Value 1
1445+
/// *[other] Value 2
1446+
/// }
1447+
/// ```
14411448
Select {
14421449
selector: InlineExpression<S>,
14431450
variants: Vec<Variant<S>>,
14441451
},
1452+
1453+
/// An inline expression such as `${ username }`:
1454+
///
1455+
/// ```ftl
1456+
/// hello-user = Hello ${ username }
1457+
/// ```
14451458
Inline(InlineExpression<S>),
14461459
}

0 commit comments

Comments
 (0)