summaryrefslogtreecommitdiffstats
path: root/third_party/rust/fluent-fallback/src/bundles.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/fluent-fallback/src/bundles.rs')
-rw-r--r--third_party/rust/fluent-fallback/src/bundles.rs426
1 files changed, 426 insertions, 0 deletions
diff --git a/third_party/rust/fluent-fallback/src/bundles.rs b/third_party/rust/fluent-fallback/src/bundles.rs
new file mode 100644
index 0000000000..7ab726d684
--- /dev/null
+++ b/third_party/rust/fluent-fallback/src/bundles.rs
@@ -0,0 +1,426 @@
+use crate::{
+ cache::{AsyncCache, Cache},
+ env::LocalesProvider,
+ errors::LocalizationError,
+ generator::{BundleGenerator, BundleIterator, BundleStream},
+ types::{L10nAttribute, L10nKey, L10nMessage, ResourceId},
+};
+use fluent_bundle::{FluentArgs, FluentBundle, FluentError};
+use rustc_hash::FxHashSet;
+use std::borrow::Cow;
+
+pub enum BundlesInner<G>
+where
+ G: BundleGenerator,
+{
+ Iter(Cache<G::Iter, G::Resource>),
+ Stream(AsyncCache<G::Stream, G::Resource>),
+}
+
+pub struct Bundles<G>(BundlesInner<G>)
+where
+ G: BundleGenerator;
+
+impl<G> Bundles<G>
+where
+ G: BundleGenerator,
+ G::Iter: BundleIterator,
+{
+ pub fn prefetch_sync(&self) {
+ match &self.0 {
+ BundlesInner::Iter(iter) => iter.prefetch(),
+ BundlesInner::Stream(_) => panic!("Can't prefetch a sync bundle set asynchronously"),
+ }
+ }
+}
+
+impl<G> Bundles<G>
+where
+ G: BundleGenerator,
+ G::Stream: BundleStream,
+{
+ pub async fn prefetch_async(&self) {
+ match &self.0 {
+ BundlesInner::Iter(_) => panic!("Can't prefetch a async bundle set synchronously"),
+ BundlesInner::Stream(stream) => stream.prefetch().await,
+ }
+ }
+}
+
+impl<G> Bundles<G>
+where
+ G: BundleGenerator,
+{
+ pub fn new<P>(sync: bool, res_ids: FxHashSet<ResourceId>, generator: &G, provider: &P) -> Self
+ where
+ G: BundleGenerator<LocalesIter = P::Iter>,
+ P: LocalesProvider,
+ {
+ Self(if sync {
+ BundlesInner::Iter(Cache::new(
+ generator.bundles_iter(provider.locales(), res_ids),
+ ))
+ } else {
+ BundlesInner::Stream(AsyncCache::new(
+ generator.bundles_stream(provider.locales(), res_ids),
+ ))
+ })
+ }
+
+ pub async fn format_value<'l>(
+ &'l self,
+ id: &'l str,
+ args: Option<&'l FluentArgs<'_>>,
+ errors: &mut Vec<LocalizationError>,
+ ) -> Option<Cow<'l, str>> {
+ match &self.0 {
+ BundlesInner::Iter(cache) => Self::format_value_from_iter(cache, id, args, errors),
+ BundlesInner::Stream(stream) => {
+ Self::format_value_from_stream(stream, id, args, errors).await
+ }
+ }
+ }
+
+ pub async fn format_values<'l>(
+ &'l self,
+ keys: &'l [L10nKey<'l>],
+ errors: &mut Vec<LocalizationError>,
+ ) -> Vec<Option<Cow<'l, str>>> {
+ match &self.0 {
+ BundlesInner::Iter(cache) => Self::format_values_from_iter(cache, keys, errors),
+ BundlesInner::Stream(stream) => {
+ Self::format_values_from_stream(stream, keys, errors).await
+ }
+ }
+ }
+
+ pub async fn format_messages<'l>(
+ &'l self,
+ keys: &'l [L10nKey<'l>],
+ errors: &mut Vec<LocalizationError>,
+ ) -> Vec<Option<L10nMessage<'l>>> {
+ match &self.0 {
+ BundlesInner::Iter(cache) => Self::format_messages_from_iter(cache, keys, errors),
+ BundlesInner::Stream(stream) => {
+ Self::format_messages_from_stream(stream, keys, errors).await
+ }
+ }
+ }
+
+ pub fn format_value_sync<'l>(
+ &'l self,
+ id: &'l str,
+ args: Option<&'l FluentArgs>,
+ errors: &mut Vec<LocalizationError>,
+ ) -> Result<Option<Cow<'l, str>>, LocalizationError> {
+ match &self.0 {
+ BundlesInner::Iter(cache) => Ok(Self::format_value_from_iter(cache, id, args, errors)),
+ BundlesInner::Stream(_) => Err(LocalizationError::SyncRequestInAsyncMode),
+ }
+ }
+
+ pub fn format_values_sync<'l>(
+ &'l self,
+ keys: &'l [L10nKey<'l>],
+ errors: &mut Vec<LocalizationError>,
+ ) -> Result<Vec<Option<Cow<'l, str>>>, LocalizationError> {
+ match &self.0 {
+ BundlesInner::Iter(cache) => Ok(Self::format_values_from_iter(cache, keys, errors)),
+ BundlesInner::Stream(_) => Err(LocalizationError::SyncRequestInAsyncMode),
+ }
+ }
+
+ pub fn format_messages_sync<'l>(
+ &'l self,
+ keys: &'l [L10nKey<'l>],
+ errors: &mut Vec<LocalizationError>,
+ ) -> Result<Vec<Option<L10nMessage<'l>>>, LocalizationError> {
+ match &self.0 {
+ BundlesInner::Iter(cache) => Ok(Self::format_messages_from_iter(cache, keys, errors)),
+ BundlesInner::Stream(_) => Err(LocalizationError::SyncRequestInAsyncMode),
+ }
+ }
+}
+
+macro_rules! format_value_from_inner {
+ ($step:expr, $id:expr, $args:expr, $errors:expr) => {
+ let mut found_message = false;
+
+ while let Some(bundle) = $step {
+ let bundle = bundle.as_ref().unwrap_or_else(|(bundle, err)| {
+ $errors.extend(err.iter().cloned().map(Into::into));
+ bundle
+ });
+
+ if let Some(msg) = bundle.get_message($id) {
+ found_message = true;
+ if let Some(value) = msg.value() {
+ let mut format_errors = vec![];
+ let result = bundle.format_pattern(value, $args, &mut format_errors);
+ if !format_errors.is_empty() {
+ $errors.push(LocalizationError::Resolver {
+ id: $id.to_string(),
+ locale: bundle.locales[0].clone(),
+ errors: format_errors,
+ });
+ }
+ return Some(result);
+ } else {
+ $errors.push(LocalizationError::MissingValue {
+ id: $id.to_string(),
+ locale: Some(bundle.locales[0].clone()),
+ });
+ }
+ } else {
+ $errors.push(LocalizationError::MissingMessage {
+ id: $id.to_string(),
+ locale: Some(bundle.locales[0].clone()),
+ });
+ }
+ }
+ if found_message {
+ $errors.push(LocalizationError::MissingValue {
+ id: $id.to_string(),
+ locale: None,
+ });
+ } else {
+ $errors.push(LocalizationError::MissingMessage {
+ id: $id.to_string(),
+ locale: None,
+ });
+ }
+ return None;
+ };
+}
+
+#[derive(Clone)]
+enum Value<'l> {
+ Present(Cow<'l, str>),
+ Missing,
+ None,
+}
+
+macro_rules! format_values_from_inner {
+ ($step:expr, $keys:expr, $errors:expr) => {
+ let mut cells = vec![Value::None; $keys.len()];
+
+ while let Some(bundle) = $step {
+ let bundle = bundle.as_ref().unwrap_or_else(|(bundle, err)| {
+ $errors.extend(err.iter().cloned().map(Into::into));
+ bundle
+ });
+
+ let mut has_missing = false;
+
+ for (key, cell) in $keys
+ .iter()
+ .zip(&mut cells)
+ .filter(|(_, cell)| !matches!(cell, Value::Present(_)))
+ {
+ if let Some(msg) = bundle.get_message(&key.id) {
+ if let Some(value) = msg.value() {
+ let mut format_errors = vec![];
+ *cell = Value::Present(bundle.format_pattern(
+ value,
+ key.args.as_ref(),
+ &mut format_errors,
+ ));
+ if !format_errors.is_empty() {
+ $errors.push(LocalizationError::Resolver {
+ id: key.id.to_string(),
+ locale: bundle.locales[0].clone(),
+ errors: format_errors,
+ });
+ }
+ } else {
+ *cell = Value::Missing;
+ has_missing = true;
+ $errors.push(LocalizationError::MissingValue {
+ id: key.id.to_string(),
+ locale: Some(bundle.locales[0].clone()),
+ });
+ }
+ } else {
+ has_missing = true;
+ $errors.push(LocalizationError::MissingMessage {
+ id: key.id.to_string(),
+ locale: Some(bundle.locales[0].clone()),
+ });
+ }
+ }
+ if !has_missing {
+ break;
+ }
+ }
+
+ return $keys
+ .iter()
+ .zip(cells)
+ .map(|(key, value)| match value {
+ Value::Present(value) => Some(value),
+ Value::Missing => {
+ $errors.push(LocalizationError::MissingValue {
+ id: key.id.to_string(),
+ locale: None,
+ });
+ None
+ }
+ Value::None => {
+ $errors.push(LocalizationError::MissingMessage {
+ id: key.id.to_string(),
+ locale: None,
+ });
+ None
+ }
+ })
+ .collect();
+ };
+}
+
+macro_rules! format_messages_from_inner {
+ ($step:expr, $keys:expr, $errors:expr) => {
+ let mut result = vec![None; $keys.len()];
+
+ let mut is_complete = false;
+
+ while let Some(bundle) = $step {
+ let bundle = bundle.as_ref().unwrap_or_else(|(bundle, err)| {
+ $errors.extend(err.iter().cloned().map(Into::into));
+ bundle
+ });
+
+ let mut has_missing = false;
+ for (key, cell) in $keys
+ .iter()
+ .zip(&mut result)
+ .filter(|(_, cell)| cell.is_none())
+ {
+ let mut format_errors = vec![];
+ let msg = Self::format_message_from_bundle(bundle, key, &mut format_errors);
+
+ if msg.is_none() {
+ has_missing = true;
+ $errors.push(LocalizationError::MissingMessage {
+ id: key.id.to_string(),
+ locale: Some(bundle.locales[0].clone()),
+ });
+ } else if !format_errors.is_empty() {
+ $errors.push(LocalizationError::Resolver {
+ id: key.id.to_string(),
+ locale: bundle.locales.get(0).cloned().unwrap(),
+ errors: format_errors,
+ });
+ }
+
+ *cell = msg;
+ }
+ if !has_missing {
+ is_complete = true;
+ break;
+ }
+ }
+
+ if !is_complete {
+ for (key, _) in $keys
+ .iter()
+ .zip(&mut result)
+ .filter(|(_, cell)| cell.is_none())
+ {
+ $errors.push(LocalizationError::MissingMessage {
+ id: key.id.to_string(),
+ locale: None,
+ });
+ }
+ }
+
+ return result;
+ };
+}
+
+impl<G> Bundles<G>
+where
+ G: BundleGenerator,
+{
+ fn format_value_from_iter<'l>(
+ cache: &'l Cache<G::Iter, G::Resource>,
+ id: &'l str,
+ args: Option<&'l FluentArgs>,
+ errors: &mut Vec<LocalizationError>,
+ ) -> Option<Cow<'l, str>> {
+ let mut bundle_iter = cache.into_iter();
+ format_value_from_inner!(bundle_iter.next(), id, args, errors);
+ }
+
+ async fn format_value_from_stream<'l>(
+ stream: &'l AsyncCache<G::Stream, G::Resource>,
+ id: &'l str,
+ args: Option<&'l FluentArgs<'_>>,
+ errors: &mut Vec<LocalizationError>,
+ ) -> Option<Cow<'l, str>> {
+ use futures::StreamExt;
+
+ let mut bundle_stream = stream.stream();
+ format_value_from_inner!(bundle_stream.next().await, id, args, errors);
+ }
+
+ async fn format_messages_from_stream<'l>(
+ stream: &'l AsyncCache<G::Stream, G::Resource>,
+ keys: &'l [L10nKey<'l>],
+ errors: &mut Vec<LocalizationError>,
+ ) -> Vec<Option<L10nMessage<'l>>> {
+ use futures::StreamExt;
+ let mut bundle_stream = stream.stream();
+ format_messages_from_inner!(bundle_stream.next().await, keys, errors);
+ }
+
+ async fn format_values_from_stream<'l>(
+ stream: &'l AsyncCache<G::Stream, G::Resource>,
+ keys: &'l [L10nKey<'l>],
+ errors: &mut Vec<LocalizationError>,
+ ) -> Vec<Option<Cow<'l, str>>> {
+ use futures::StreamExt;
+ let mut bundle_stream = stream.stream();
+
+ format_values_from_inner!(bundle_stream.next().await, keys, errors);
+ }
+
+ fn format_message_from_bundle<'l>(
+ bundle: &'l FluentBundle<G::Resource>,
+ key: &'l L10nKey,
+ format_errors: &mut Vec<FluentError>,
+ ) -> Option<L10nMessage<'l>> {
+ let msg = bundle.get_message(&key.id)?;
+ let value = msg
+ .value()
+ .map(|pattern| bundle.format_pattern(pattern, key.args.as_ref(), format_errors));
+ let attributes = msg
+ .attributes()
+ .map(|attr| {
+ let value = bundle.format_pattern(attr.value(), key.args.as_ref(), format_errors);
+ L10nAttribute {
+ name: attr.id().into(),
+ value,
+ }
+ })
+ .collect();
+ Some(L10nMessage { value, attributes })
+ }
+
+ fn format_messages_from_iter<'l>(
+ cache: &'l Cache<G::Iter, G::Resource>,
+ keys: &'l [L10nKey<'l>],
+ errors: &mut Vec<LocalizationError>,
+ ) -> Vec<Option<L10nMessage<'l>>> {
+ let mut bundle_iter = cache.into_iter();
+ format_messages_from_inner!(bundle_iter.next(), keys, errors);
+ }
+
+ fn format_values_from_iter<'l>(
+ cache: &'l Cache<G::Iter, G::Resource>,
+ keys: &'l [L10nKey<'l>],
+ errors: &mut Vec<LocalizationError>,
+ ) -> Vec<Option<Cow<'l, str>>> {
+ let mut bundle_iter = cache.into_iter();
+ format_values_from_inner!(bundle_iter.next(), keys, errors);
+ }
+}