use crate::env::ErrorReporter; use crate::errors::L10nRegistryError; use crate::fluent::FluentBundle; use crate::registry::BundleAdapter; use crate::registry::L10nRegistry; use crate::source::FileFetcher; use async_trait::async_trait; use fluent_fallback::{env::LocalesProvider, types::ResourceId}; use fluent_testing::MockFileSystem; use std::cell::RefCell; use std::rc::Rc; use unic_langid::LanguageIdentifier; pub struct RegistrySetup { pub name: String, pub file_sources: Vec, pub locales: Vec, } pub struct FileSource { pub name: String, pub metasource: String, pub locales: Vec, pub path_scheme: String, } #[derive(Clone)] pub struct MockBundleAdapter; impl BundleAdapter for MockBundleAdapter { fn adapt_bundle(&self, _bundle: &mut FluentBundle) {} } impl FileSource { pub fn new( name: S, metasource: Option, locales: Vec, path_scheme: S, ) -> Self where S: ToString, { let metasource = match metasource { Some(s) => s.to_string(), None => String::default(), }; Self { name: name.to_string(), metasource, locales, path_scheme: path_scheme.to_string(), } } } impl RegistrySetup { pub fn new( name: &str, file_sources: Vec, locales: Vec, ) -> Self { Self { name: name.to_string(), file_sources, locales, } } } impl From for RegistrySetup { fn from(s: fluent_testing::scenarios::structs::Scenario) -> Self { Self { name: s.name, file_sources: s .file_sources .into_iter() .map(|source| { FileSource::new( source.name, None, source .locales .into_iter() .map(|l| l.parse().unwrap()) .collect(), source.path_scheme, ) }) .collect(), locales: s .locales .into_iter() .map(|loc| loc.parse().unwrap()) .collect(), } } } impl From<&fluent_testing::scenarios::structs::Scenario> for RegistrySetup { fn from(s: &fluent_testing::scenarios::structs::Scenario) -> Self { Self { name: s.name.clone(), file_sources: s .file_sources .iter() .map(|source| { FileSource::new( source.name.clone(), None, source.locales.iter().map(|l| l.parse().unwrap()).collect(), source.path_scheme.clone(), ) }) .collect(), locales: s.locales.iter().map(|loc| loc.parse().unwrap()).collect(), } } } #[derive(Default)] struct InnerFileFetcher { fs: MockFileSystem, } #[derive(Clone)] pub struct TestFileFetcher { inner: Rc, } impl TestFileFetcher { pub fn new() -> Self { Self { inner: Rc::new(InnerFileFetcher::default()), } } pub fn get_test_file_source( &self, name: &str, metasource: Option, locales: Vec, path: &str, ) -> crate::source::FileSource { crate::source::FileSource::new( name.to_string(), metasource, locales, path.to_string(), Default::default(), self.clone(), ) } pub fn get_test_file_source_with_index( &self, name: &str, metasource: Option, locales: Vec, path: &str, index: Vec<&str>, ) -> crate::source::FileSource { crate::source::FileSource::new_with_index( name.to_string(), metasource, locales, path.to_string(), Default::default(), self.clone(), index.into_iter().map(|s| s.to_string()).collect(), ) } pub fn get_registry(&self, setup: S) -> L10nRegistry where S: Into, { self.get_registry_and_environment(setup).1 } pub fn get_registry_and_environment( &self, setup: S, ) -> ( TestEnvironment, L10nRegistry, ) where S: Into, { let setup: RegistrySetup = setup.into(); let provider = TestEnvironment::new(setup.locales); let reg = L10nRegistry::with_provider(provider.clone()); let sources = setup .file_sources .into_iter() .map(|source| { let mut s = self.get_test_file_source( &source.name, Some(source.metasource), source.locales, &source.path_scheme, ); s.set_reporter(provider.clone()); s }) .collect(); reg.register_sources(sources).unwrap(); (provider, reg) } pub fn get_registry_and_environment_with_adapter( &self, setup: S, bundle_adapter: B, ) -> (TestEnvironment, L10nRegistry) where S: Into, B: BundleAdapter, { let setup: RegistrySetup = setup.into(); let provider = TestEnvironment::new(setup.locales); let mut reg = L10nRegistry::with_provider(provider.clone()); let sources = setup .file_sources .into_iter() .map(|source| { let mut s = self.get_test_file_source( &source.name, None, source.locales, &source.path_scheme, ); s.set_reporter(provider.clone()); s }) .collect(); reg.register_sources(sources).unwrap(); reg.set_bundle_adapter(bundle_adapter) .expect("Failed to set bundle adapter."); (provider, reg) } } #[async_trait(?Send)] impl FileFetcher for TestFileFetcher { fn fetch_sync(&self, resource_id: &ResourceId) -> std::io::Result { self.inner.fs.get_test_file_sync(&resource_id.value) } async fn fetch(&self, resource_id: &ResourceId) -> std::io::Result { self.inner.fs.get_test_file_async(&resource_id.value).await } } pub enum ErrorStrategy { Panic, Report, Nothing, } pub struct InnerTestEnvironment { locales: Vec, errors: Vec, error_strategy: ErrorStrategy, } #[derive(Clone)] pub struct TestEnvironment { inner: Rc>, } impl TestEnvironment { pub fn new(locales: Vec) -> Self { Self { inner: Rc::new(RefCell::new(InnerTestEnvironment { locales, errors: vec![], error_strategy: ErrorStrategy::Report, })), } } pub fn set_locales(&self, locales: Vec) { self.inner.borrow_mut().locales = locales; } pub fn errors(&self) -> Vec { self.inner.borrow().errors.clone() } pub fn clear_errors(&self) { self.inner.borrow_mut().errors.clear() } } impl LocalesProvider for TestEnvironment { type Iter = std::vec::IntoIter; fn locales(&self) -> Self::Iter { self.inner.borrow().locales.clone().into_iter() } } impl ErrorReporter for TestEnvironment { fn report_errors(&self, errors: Vec) { match self.inner.borrow().error_strategy { ErrorStrategy::Panic => { panic!("Errors: {:#?}", errors); } ErrorStrategy::Report => { #[cfg(test)] // Don't let printing affect benchmarks eprintln!("Errors: {:#?}", errors); } ErrorStrategy::Nothing => {} } self.inner.borrow_mut().errors.extend(errors); } }