diff options
Diffstat (limited to 'third_party/rust/fluent-fallback/src/bundles.rs')
-rw-r--r-- | third_party/rust/fluent-fallback/src/bundles.rs | 426 |
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); + } +} |