lib.rs (8795B)
1 use l10nregistry::env::ErrorReporter; 2 use l10nregistry::errors::L10nRegistryError; 3 use l10nregistry::fluent::FluentBundle; 4 use l10nregistry::registry::BundleAdapter; 5 use l10nregistry::registry::L10nRegistry; 6 use l10nregistry::source::FileFetcher; 7 use async_trait::async_trait; 8 use fluent_fallback::{env::LocalesProvider, types::ResourceId}; 9 use fluent_testing::MockFileSystem; 10 use std::cell::RefCell; 11 use std::rc::Rc; 12 use unic_langid::LanguageIdentifier; 13 14 pub mod solver; 15 16 pub struct RegistrySetup { 17 pub name: String, 18 pub file_sources: Vec<FileSource>, 19 pub locales: Vec<LanguageIdentifier>, 20 } 21 22 pub struct FileSource { 23 pub name: String, 24 pub metasource: String, 25 pub locales: Vec<LanguageIdentifier>, 26 pub path_scheme: String, 27 } 28 29 #[derive(Clone)] 30 pub struct MockBundleAdapter; 31 32 impl BundleAdapter for MockBundleAdapter { 33 fn adapt_bundle(&self, _bundle: &mut FluentBundle) {} 34 } 35 36 impl FileSource { 37 pub fn new<S>( 38 name: S, 39 metasource: Option<S>, 40 locales: Vec<LanguageIdentifier>, 41 path_scheme: S, 42 ) -> Self 43 where 44 S: ToString, 45 { 46 let metasource = match metasource { 47 Some(s) => s.to_string(), 48 None => String::default(), 49 }; 50 51 Self { 52 name: name.to_string(), 53 metasource, 54 locales, 55 path_scheme: path_scheme.to_string(), 56 } 57 } 58 } 59 60 impl RegistrySetup { 61 pub fn new( 62 name: &str, 63 file_sources: Vec<FileSource>, 64 locales: Vec<LanguageIdentifier>, 65 ) -> Self { 66 Self { 67 name: name.to_string(), 68 file_sources, 69 locales, 70 } 71 } 72 } 73 74 impl From<fluent_testing::scenarios::structs::Scenario> for RegistrySetup { 75 fn from(s: fluent_testing::scenarios::structs::Scenario) -> Self { 76 Self { 77 name: s.name, 78 file_sources: s 79 .file_sources 80 .into_iter() 81 .map(|source| { 82 FileSource::new( 83 source.name, 84 None, 85 source 86 .locales 87 .into_iter() 88 .map(|l| l.parse().unwrap()) 89 .collect(), 90 source.path_scheme, 91 ) 92 }) 93 .collect(), 94 locales: s 95 .locales 96 .into_iter() 97 .map(|loc| loc.parse().unwrap()) 98 .collect(), 99 } 100 } 101 } 102 103 impl From<&fluent_testing::scenarios::structs::Scenario> for RegistrySetup { 104 fn from(s: &fluent_testing::scenarios::structs::Scenario) -> Self { 105 Self { 106 name: s.name.clone(), 107 file_sources: s 108 .file_sources 109 .iter() 110 .map(|source| { 111 FileSource::new( 112 source.name.clone(), 113 None, 114 source.locales.iter().map(|l| l.parse().unwrap()).collect(), 115 source.path_scheme.clone(), 116 ) 117 }) 118 .collect(), 119 locales: s.locales.iter().map(|loc| loc.parse().unwrap()).collect(), 120 } 121 } 122 } 123 124 #[derive(Default)] 125 struct InnerFileFetcher { 126 fs: MockFileSystem, 127 } 128 129 #[derive(Clone)] 130 pub struct TestFileFetcher { 131 inner: Rc<InnerFileFetcher>, 132 } 133 134 impl TestFileFetcher { 135 pub fn new() -> Self { 136 Self { 137 inner: Rc::new(InnerFileFetcher::default()), 138 } 139 } 140 141 pub fn get_test_file_source( 142 &self, 143 name: &str, 144 metasource: Option<String>, 145 locales: Vec<LanguageIdentifier>, 146 path: &str, 147 ) -> l10nregistry::source::FileSource { 148 l10nregistry::source::FileSource::new( 149 name.to_string(), 150 metasource, 151 locales, 152 path.to_string(), 153 Default::default(), 154 self.clone(), 155 ) 156 } 157 158 pub fn get_test_file_source_with_index( 159 &self, 160 name: &str, 161 metasource: Option<String>, 162 locales: Vec<LanguageIdentifier>, 163 path: &str, 164 index: Vec<&str>, 165 ) -> l10nregistry::source::FileSource { 166 l10nregistry::source::FileSource::new_with_index( 167 name.to_string(), 168 metasource, 169 locales, 170 path.to_string(), 171 Default::default(), 172 self.clone(), 173 index.into_iter().map(|s| s.to_string()).collect(), 174 ) 175 } 176 177 pub fn get_registry<S>(&self, setup: S) -> L10nRegistry<TestEnvironment, MockBundleAdapter> 178 where 179 S: Into<RegistrySetup>, 180 { 181 self.get_registry_and_environment(setup).1 182 } 183 184 pub fn get_registry_and_environment<S>( 185 &self, 186 setup: S, 187 ) -> ( 188 TestEnvironment, 189 L10nRegistry<TestEnvironment, MockBundleAdapter>, 190 ) 191 where 192 S: Into<RegistrySetup>, 193 { 194 let setup: RegistrySetup = setup.into(); 195 let provider = TestEnvironment::new(setup.locales); 196 197 let reg = L10nRegistry::with_provider(provider.clone()); 198 let sources = setup 199 .file_sources 200 .into_iter() 201 .map(|source| { 202 let mut s = self.get_test_file_source( 203 &source.name, 204 Some(source.metasource), 205 source.locales, 206 &source.path_scheme, 207 ); 208 s.set_reporter(provider.clone()); 209 s 210 }) 211 .collect(); 212 reg.register_sources(sources).unwrap(); 213 (provider, reg) 214 } 215 216 pub fn get_registry_and_environment_with_adapter<S, B>( 217 &self, 218 setup: S, 219 bundle_adapter: B, 220 ) -> (TestEnvironment, L10nRegistry<TestEnvironment, B>) 221 where 222 S: Into<RegistrySetup>, 223 B: BundleAdapter, 224 { 225 let setup: RegistrySetup = setup.into(); 226 let provider = TestEnvironment::new(setup.locales); 227 228 let mut reg = L10nRegistry::with_provider(provider.clone()); 229 let sources = setup 230 .file_sources 231 .into_iter() 232 .map(|source| { 233 let mut s = self.get_test_file_source( 234 &source.name, 235 None, 236 source.locales, 237 &source.path_scheme, 238 ); 239 s.set_reporter(provider.clone()); 240 s 241 }) 242 .collect(); 243 reg.register_sources(sources).unwrap(); 244 reg.set_bundle_adapter(bundle_adapter) 245 .expect("Failed to set bundle adapter."); 246 (provider, reg) 247 } 248 } 249 250 #[async_trait(?Send)] 251 impl FileFetcher for TestFileFetcher { 252 fn fetch_sync(&self, resource_id: &ResourceId) -> std::io::Result<String> { 253 self.inner.fs.get_test_file_sync(&resource_id.value) 254 } 255 256 async fn fetch(&self, resource_id: &ResourceId) -> std::io::Result<String> { 257 self.inner.fs.get_test_file_async(&resource_id.value).await 258 } 259 } 260 261 pub enum ErrorStrategy { 262 Panic, 263 Report, 264 Nothing, 265 } 266 267 pub struct InnerTestEnvironment { 268 locales: Vec<LanguageIdentifier>, 269 errors: Vec<L10nRegistryError>, 270 error_strategy: ErrorStrategy, 271 } 272 273 #[derive(Clone)] 274 pub struct TestEnvironment { 275 inner: Rc<RefCell<InnerTestEnvironment>>, 276 } 277 278 impl TestEnvironment { 279 pub fn new(locales: Vec<LanguageIdentifier>) -> Self { 280 Self { 281 inner: Rc::new(RefCell::new(InnerTestEnvironment { 282 locales, 283 errors: vec![], 284 error_strategy: ErrorStrategy::Report, 285 })), 286 } 287 } 288 289 pub fn set_locales(&self, locales: Vec<LanguageIdentifier>) { 290 self.inner.borrow_mut().locales = locales; 291 } 292 293 pub fn errors(&self) -> Vec<L10nRegistryError> { 294 self.inner.borrow().errors.clone() 295 } 296 297 pub fn clear_errors(&self) { 298 self.inner.borrow_mut().errors.clear() 299 } 300 } 301 302 impl LocalesProvider for TestEnvironment { 303 type Iter = std::vec::IntoIter<LanguageIdentifier>; 304 305 fn locales(&self) -> Self::Iter { 306 self.inner.borrow().locales.clone().into_iter() 307 } 308 } 309 310 impl ErrorReporter for TestEnvironment { 311 fn report_errors(&self, errors: Vec<L10nRegistryError>) { 312 match self.inner.borrow().error_strategy { 313 ErrorStrategy::Panic => { 314 panic!("Errors: {:#?}", errors); 315 } 316 ErrorStrategy::Report => { 317 #[cfg(test)] // Don't let printing affect benchmarks 318 eprintln!("Errors: {:#?}", errors); 319 } 320 ErrorStrategy::Nothing => {} 321 } 322 self.inner.borrow_mut().errors.extend(errors); 323 } 324 }