tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

synchronous.rs (10531B)


      1 use super::{BundleAdapter, L10nRegistry, MetaSources};
      2 use crate::env::ErrorReporter;
      3 use crate::errors::L10nRegistryError;
      4 use crate::fluent::{FluentBundle, FluentError};
      5 use crate::solver::{SerialProblemSolver, SyncTester};
      6 use crate::source::ResourceOption;
      7 use fluent_fallback::{generator::BundleIterator, types::ResourceId};
      8 use unic_langid::LanguageIdentifier;
      9 
     10 impl MetaSources {
     11    pub(crate) fn bundle_from_order<P, B>(
     12        &self,
     13        metasource: usize,
     14        locale: LanguageIdentifier,
     15        source_order: &[usize],
     16        resource_ids: &[ResourceId],
     17        error_reporter: &P,
     18        bundle_adapter: Option<&B>,
     19    ) -> Option<Result<FluentBundle, (FluentBundle, Vec<FluentError>)>>
     20    where
     21        P: ErrorReporter,
     22        B: BundleAdapter,
     23    {
     24        let mut bundle = FluentBundle::new(vec![locale.clone()]);
     25 
     26        if let Some(bundle_adapter) = bundle_adapter {
     27            bundle_adapter.adapt_bundle(&mut bundle);
     28        }
     29 
     30        let mut errors = vec![];
     31 
     32        for (&source_idx, resource_id) in source_order.iter().zip(resource_ids.iter()) {
     33            let source = self.filesource(metasource, source_idx);
     34            if let ResourceOption::Some(res) =
     35                source.fetch_file_sync(&locale, resource_id, /* overload */ true)
     36            {
     37                if source.options.allow_override {
     38                    bundle.add_resource_overriding(res);
     39                } else if let Err(err) = bundle.add_resource(res) {
     40                    errors.extend(err.into_iter().map(|error| L10nRegistryError::FluentError {
     41                        resource_id: resource_id.clone(),
     42                        loc: None,
     43                        error,
     44                    }));
     45                }
     46            } else if resource_id.is_required() {
     47                return None;
     48            }
     49        }
     50 
     51        if !errors.is_empty() {
     52            error_reporter.report_errors(errors);
     53        }
     54        Some(Ok(bundle))
     55    }
     56 }
     57 
     58 impl<P, B> L10nRegistry<P, B>
     59 where
     60    P: Clone,
     61    B: Clone,
     62 {
     63    /// A test-only function for easily generating bundles for a single langid.
     64    #[cfg(feature = "test-fluent")]
     65    pub fn generate_bundles_for_lang_sync(
     66        &self,
     67        langid: LanguageIdentifier,
     68        resource_ids: Vec<ResourceId>,
     69    ) -> GenerateBundlesSync<P, B> {
     70        let lang_ids = vec![langid];
     71 
     72        GenerateBundlesSync::new(self.clone(), lang_ids.into_iter(), resource_ids)
     73    }
     74 
     75    /// Wiring for hooking up the synchronous bundle generation to the
     76    /// [BundleGenerator] trait.
     77    pub fn generate_bundles_sync(
     78        &self,
     79        locales: std::vec::IntoIter<LanguageIdentifier>,
     80        resource_ids: Vec<ResourceId>,
     81    ) -> GenerateBundlesSync<P, B> {
     82        GenerateBundlesSync::new(self.clone(), locales, resource_ids)
     83    }
     84 }
     85 
     86 enum State {
     87    Empty,
     88    Locale(LanguageIdentifier),
     89    Solver {
     90        locale: LanguageIdentifier,
     91        solver: SerialProblemSolver,
     92    },
     93 }
     94 
     95 impl Default for State {
     96    fn default() -> Self {
     97        Self::Empty
     98    }
     99 }
    100 
    101 impl State {
    102    fn get_locale(&self) -> &LanguageIdentifier {
    103        match self {
    104            Self::Locale(locale) => locale,
    105            Self::Solver { locale, .. } => locale,
    106            Self::Empty => unreachable!("Attempting to get a locale for an empty state."),
    107        }
    108    }
    109 
    110    fn take_solver(&mut self) -> SerialProblemSolver {
    111        replace_with::replace_with_or_default_and_return(self, |self_| match self_ {
    112            Self::Solver { locale, solver } => (solver, Self::Locale(locale)),
    113            _ => unreachable!("Attempting to take a solver in an invalid state."),
    114        })
    115    }
    116 
    117    fn put_back_solver(&mut self, solver: SerialProblemSolver) {
    118        replace_with::replace_with_or_default(self, |self_| match self_ {
    119            Self::Locale(locale) => Self::Solver { locale, solver },
    120            _ => unreachable!("Attempting to put back a solver in an invalid state."),
    121        })
    122    }
    123 }
    124 
    125 pub struct GenerateBundlesSync<P, B> {
    126    reg: L10nRegistry<P, B>,
    127    locales: std::vec::IntoIter<LanguageIdentifier>,
    128    current_metasource: usize,
    129    resource_ids: Vec<ResourceId>,
    130    state: State,
    131 }
    132 
    133 impl<P, B> GenerateBundlesSync<P, B> {
    134    fn new(
    135        reg: L10nRegistry<P, B>,
    136        locales: std::vec::IntoIter<LanguageIdentifier>,
    137        resource_ids: Vec<ResourceId>,
    138    ) -> Self {
    139        Self {
    140            reg,
    141            locales,
    142            current_metasource: 0,
    143            resource_ids,
    144            state: State::Empty,
    145        }
    146    }
    147 }
    148 
    149 impl<P, B> SyncTester for GenerateBundlesSync<P, B> {
    150    fn test_sync(&self, res_idx: usize, source_idx: usize) -> bool {
    151        let locale = self.state.get_locale();
    152        let resource_id = &self.resource_ids[res_idx];
    153        !self
    154            .reg
    155            .try_borrow_metasources()
    156            .expect("Unable to get the MetaSources.")
    157            .filesource(self.current_metasource, source_idx)
    158            .fetch_file_sync(locale, resource_id, /* overload */ true)
    159            .is_required_and_missing()
    160    }
    161 }
    162 
    163 impl<P, B> BundleIterator for GenerateBundlesSync<P, B>
    164 where
    165    P: ErrorReporter,
    166 {
    167    fn prefetch_sync(&mut self) {
    168        if let State::Solver { .. } = self.state {
    169            let mut solver = self.state.take_solver();
    170            if let Err(idx) = solver.try_next(self, true) {
    171                self.reg
    172                    .shared
    173                    .provider
    174                    .report_errors(vec![L10nRegistryError::MissingResource {
    175                        locale: self.state.get_locale().clone(),
    176                        resource_id: self.resource_ids[idx].clone(),
    177                    }]);
    178            }
    179            self.state.put_back_solver(solver);
    180            return;
    181        }
    182 
    183        if let Some(locale) = self.locales.next() {
    184            let mut solver = SerialProblemSolver::new(
    185                self.resource_ids.len(),
    186                self.reg
    187                    .try_borrow_metasources()
    188                    .expect("Unable to get the MetaSources.")
    189                    .get(self.current_metasource)
    190                    .len(),
    191            );
    192            self.state = State::Locale(locale.clone());
    193            if let Err(idx) = solver.try_next(self, true) {
    194                self.reg
    195                    .shared
    196                    .provider
    197                    .report_errors(vec![L10nRegistryError::MissingResource {
    198                        locale,
    199                        resource_id: self.resource_ids[idx].clone(),
    200                    }]);
    201            }
    202            self.state.put_back_solver(solver);
    203        }
    204    }
    205 }
    206 
    207 impl<P, B> Iterator for GenerateBundlesSync<P, B>
    208 where
    209    P: ErrorReporter,
    210    B: BundleAdapter,
    211 {
    212    type Item = Result<FluentBundle, (FluentBundle, Vec<FluentError>)>;
    213 
    214    /// Synchronously generate a bundle based on a solver.
    215    fn next(&mut self) -> Option<Self::Item> {
    216        let metasources = self
    217            .reg
    218            .try_borrow_metasources()
    219            .expect("Unable to get the MetaSources.");
    220 
    221        if metasources.is_empty() {
    222            // There are no metasources available, so no bundles can be generated.
    223            return None;
    224        }
    225 
    226        loop {
    227            if let State::Solver { .. } = self.state {
    228                // A solver has already been set up, continue iterating through the
    229                // resources and generating a bundle.
    230                let mut solver = self.state.take_solver();
    231                let solver_result = solver.try_next(self, false);
    232 
    233                if let Ok(Some(order)) = solver_result {
    234                    // The solver resolved an ordering, and a bundle may be able
    235                    // to be generated.
    236 
    237                    let bundle = metasources.bundle_from_order(
    238                        self.current_metasource,
    239                        self.state.get_locale().clone(),
    240                        &order,
    241                        &self.resource_ids,
    242                        &self.reg.shared.provider,
    243                        self.reg.shared.bundle_adapter.as_ref(),
    244                    );
    245 
    246                    self.state.put_back_solver(solver);
    247 
    248                    if bundle.is_some() {
    249                        // The bundle was successfully generated.
    250                        return bundle;
    251                    }
    252 
    253                    // No bundle was generated, continue on.
    254                    continue;
    255                }
    256 
    257                // There is no bundle ordering available.
    258 
    259                if self.current_metasource > 0 {
    260                    // There are more metasources, create a new solver and try the
    261                    // next metasource. If there is an error in the solver_result
    262                    // ignore it for now, since there are more metasources.
    263                    self.current_metasource -= 1;
    264                    let solver = SerialProblemSolver::new(
    265                        self.resource_ids.len(),
    266                        metasources.get(self.current_metasource).len(),
    267                    );
    268                    self.state = State::Solver {
    269                        locale: self.state.get_locale().clone(),
    270                        solver,
    271                    };
    272                    continue;
    273                }
    274 
    275                if let Err(idx) = solver_result {
    276                    // Since there are no more metasources, and there is an error,
    277                    // report it instead of ignoring it.
    278                    self.reg.shared.provider.report_errors(vec![
    279                        L10nRegistryError::MissingResource {
    280                            locale: self.state.get_locale().clone(),
    281                            resource_id: self.resource_ids[idx].clone(),
    282                        },
    283                    ]);
    284                }
    285 
    286                self.state = State::Empty;
    287                continue;
    288            }
    289 
    290            // Try the next locale, or break out of the loop if there are none left.
    291            let locale = self.locales.next()?;
    292 
    293            // Restart at the end of the metasources for this locale, and iterate
    294            // backwards.
    295            let last_metasource_idx = metasources.len() - 1;
    296            self.current_metasource = last_metasource_idx;
    297 
    298            let solver = SerialProblemSolver::new(
    299                self.resource_ids.len(),
    300                metasources.get(self.current_metasource).len(),
    301            );
    302 
    303            // Continue iterating on the next solver.
    304            self.state = State::Solver { locale, solver };
    305        }
    306    }
    307 }