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)> { 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> { 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) } }