summaryrefslogtreecommitdiffstats
path: root/third_party/rust/fluent-bundle/src/resource.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/fluent-bundle/src/resource.rs')
-rw-r--r--third_party/rust/fluent-bundle/src/resource.rs171
1 files changed, 171 insertions, 0 deletions
diff --git a/third_party/rust/fluent-bundle/src/resource.rs b/third_party/rust/fluent-bundle/src/resource.rs
new file mode 100644
index 0000000000..0c39c838f4
--- /dev/null
+++ b/third_party/rust/fluent-bundle/src/resource.rs
@@ -0,0 +1,171 @@
+use fluent_syntax::ast;
+use fluent_syntax::parser::{parse_runtime, ParserError};
+
+use self_cell::self_cell;
+
+type Resource<'s> = ast::Resource<&'s str>;
+
+self_cell!(
+ pub struct InnerFluentResource {
+ owner: String,
+
+ #[covariant]
+ dependent: Resource,
+ }
+
+ impl {Debug}
+);
+
+/// A resource containing a list of localization messages.
+///
+/// [`FluentResource`] wraps an [`Abstract Syntax Tree`](../fluent_syntax/ast/index.html) produced by the
+/// [`parser`](../fluent_syntax/parser/index.html) and provides an access to a list
+/// of its entries.
+///
+/// A good mental model for a resource is a single FTL file, but in the future
+/// there's nothing preventing a resource from being stored in a data base,
+/// pre-parsed format or in some other structured form.
+///
+/// # Example
+///
+/// ```
+/// use fluent_bundle::FluentResource;
+///
+/// let source = r#"
+///
+/// hello-world = Hello World!
+///
+/// "#;
+///
+/// let resource = FluentResource::try_new(source.to_string())
+/// .expect("Errors encountered while parsing a resource.");
+///
+/// assert_eq!(resource.entries().count(), 1);
+/// ```
+///
+/// # Ownership
+///
+/// A resource owns the source string and the AST contains references
+/// to the slices of the source.
+#[derive(Debug)]
+pub struct FluentResource(InnerFluentResource);
+
+impl FluentResource {
+ /// A fallible constructor of a new [`FluentResource`].
+ ///
+ /// It takes an encoded `Fluent Translation List` string, parses
+ /// it and stores both, the input string and the AST view of it,
+ /// for runtime use.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use fluent_bundle::FluentResource;
+ ///
+ /// let source = r#"
+ ///
+ /// hello-world = Hello, { $user }!
+ ///
+ /// "#;
+ ///
+ /// let resource = FluentResource::try_new(source.to_string());
+ ///
+ /// assert!(resource.is_ok());
+ /// ```
+ ///
+ /// # Errors
+ ///
+ /// The method will return the resource irrelevant of parse errors
+ /// encountered during parsing of the source, but in case of errors,
+ /// the `Err` variant will contain both the structure and a vector
+ /// of errors.
+ pub fn try_new(source: String) -> Result<Self, (Self, Vec<ParserError>)> {
+ let mut errors = None;
+
+ let res = InnerFluentResource::new(source, |source| match parse_runtime(source.as_str()) {
+ Ok(ast) => ast,
+ Err((ast, err)) => {
+ errors = Some(err);
+ ast
+ }
+ });
+
+ match errors {
+ None => Ok(Self(res)),
+ Some(err) => Err((Self(res), err)),
+ }
+ }
+
+ /// Returns a reference to the source string that was used
+ /// to construct the [`FluentResource`].
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use fluent_bundle::FluentResource;
+ ///
+ /// let source = "hello-world = Hello, { $user }!";
+ ///
+ /// let resource = FluentResource::try_new(source.to_string())
+ /// .expect("Failed to parse FTL.");
+ ///
+ /// assert_eq!(
+ /// resource.source(),
+ /// "hello-world = Hello, { $user }!"
+ /// );
+ /// ```
+ pub fn source(&self) -> &str {
+ &self.0.borrow_owner()
+ }
+
+ /// Returns an iterator over [`entries`](fluent_syntax::ast::Entry) of the [`FluentResource`].
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use fluent_bundle::FluentResource;
+ /// use fluent_syntax::ast;
+ ///
+ /// let source = r#"
+ ///
+ /// hello-world = Hello, { $user }!
+ ///
+ /// "#;
+ ///
+ /// let resource = FluentResource::try_new(source.to_string())
+ /// .expect("Failed to parse FTL.");
+ ///
+ /// assert_eq!(
+ /// resource.entries().count(),
+ /// 1
+ /// );
+ /// assert!(matches!(resource.entries().next(), Some(ast::Entry::Message(_))));
+ /// ```
+ pub fn entries(&self) -> impl Iterator<Item = &ast::Entry<&str>> {
+ self.0.borrow_dependent().body.iter()
+ }
+
+ /// Returns an [`Entry`](fluent_syntax::ast::Entry) at the
+ /// given index out of the [`FluentResource`].
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use fluent_bundle::FluentResource;
+ /// use fluent_syntax::ast;
+ ///
+ /// let source = r#"
+ ///
+ /// hello-world = Hello, { $user }!
+ ///
+ /// "#;
+ ///
+ /// let resource = FluentResource::try_new(source.to_string())
+ /// .expect("Failed to parse FTL.");
+ ///
+ /// assert!(matches!(resource.get_entry(0), Some(ast::Entry::Message(_))));
+ /// ```
+ pub fn get_entry(&self, idx: usize) -> Option<&ast::Entry<&str>> {
+ self.0.borrow_dependent().body.get(idx)
+ }
+}