#![feature(round_char_boundary)] #![feature(exact_size_is_empty)] use std::io::Read; use std::path::Path; use std::path::PathBuf; use futures::future::BoxFuture; use futures::future::FutureExt; use organic::compare::run_anonymous_compare; use organic::compare::run_compare_on_file; use tokio::sync::Semaphore; use tokio::task::JoinError; use walkdir::WalkDir; #[cfg(feature = "tracing")] use crate::init_tracing::init_telemetry; #[cfg(feature = "tracing")] use crate::init_tracing::shutdown_telemetry; #[cfg(feature = "tracing")] mod init_tracing; #[cfg(not(feature = "tracing"))] fn main() -> Result<(), Box> { let rt = tokio::runtime::Runtime::new()?; let result = rt.block_on(async { let main_body_result = main_body().await; main_body_result }); result } #[cfg(feature = "tracing")] fn main() -> Result<(), Box> { let rt = tokio::runtime::Runtime::new()?; let result = rt.block_on(async { init_telemetry()?; let main_body_result = main_body().await; shutdown_telemetry()?; main_body_result }); result } #[cfg_attr(feature = "tracing", tracing::instrument(ret, level = "debug"))] async fn main_body() -> Result<(), Box> { let layer = compare_group("org-mode", || { compare_all_org_document("/foreign_documents/org-mode") }); let layer = layer.chain(compare_group("emacs", || { compare_all_org_document("/foreign_documents/emacs") })); let running_tests: Vec<_> = layer.map(|c| tokio::spawn(c.run_test())).collect(); for test in running_tests.into_iter() { let test_result = test.await??; println!("{:?}", test_result); } Ok(()) } fn compare_group, F: Fn() -> I, I: Iterator>( name: N, inner: F, ) -> impl Iterator { std::iter::once(TestConfig::TestLayer(TestLayer { name: name.into(), children: inner().collect(), })) } fn compare_all_org_document>(root_dir: P) -> impl Iterator { let root_dir = root_dir.as_ref(); let test_files = WalkDir::new(root_dir) .into_iter() .filter(|e| match e { Ok(dir_entry) => { dir_entry.file_type().is_file() && Path::new(dir_entry.file_name()) .extension() .map(|ext| ext.to_ascii_lowercase() == "org") .unwrap_or(false) } Err(_) => true, }) .collect::, _>>() .unwrap(); let test_configs = test_files.into_iter().map(|test_file| { let name = test_file.path().as_os_str().to_string_lossy().into_owned(); TestConfig::SingleFile(SingleFile { name, file_path: test_file.into_path(), }) }); test_configs } static TEST_PERMITS: Semaphore = Semaphore::const_new(8); #[derive(Debug)] enum TestConfig { TestLayer(TestLayer), SingleFile(SingleFile), } #[derive(Debug)] struct TestLayer { name: String, children: Vec, } #[derive(Debug)] struct SingleFile { name: String, file_path: PathBuf, } #[derive(Debug)] enum TestResult { ResultLayer(ResultLayer), SingleFileResult(SingleFileResult), } #[derive(Debug)] struct ResultLayer { name: String, children: Vec, } #[derive(Debug)] struct SingleFileResult { name: String, file_path: PathBuf, status: TestStatus, } #[derive(Debug)] pub(crate) enum TestStatus { Pass, Fail, } impl TestConfig { fn run_test(self) -> BoxFuture<'static, Result> { async move { match self { TestConfig::TestLayer(test) => Ok(TestResult::ResultLayer(test.run_test().await?)), TestConfig::SingleFile(test) => { Ok(TestResult::SingleFileResult(test.run_test().await?)) } } } .boxed() } } impl SingleFile { async fn run_test(self) -> Result { let _permit = TEST_PERMITS.acquire().await.unwrap(); let result = run_compare_on_file(&self.file_path); Ok(SingleFileResult { name: self.name, file_path: self.file_path, status: if result.is_ok() { TestStatus::Pass } else { TestStatus::Fail }, }) } } impl TestLayer { async fn run_test(self) -> Result { let running_children: Vec<_> = self .children .into_iter() .map(|c| tokio::spawn(c.run_test())) .collect(); let mut children = Vec::with_capacity(running_children.len()); for c in running_children { children.push(c.await??); } Ok(ResultLayer { name: self.name, children, }) } }