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 }