1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 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)
}
}