use dprint_cli_core::types::ErrBox;
use std::thread;
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use crate::environment::Environment;
use crate::plugins::{InitializedPlugin, InitializedPluginPool, PluginPools, TakePluginResult};
use crate::utils::ErrorCountLogger;
use super::{LongFormatCheckerThread, WorkerRegistry, Worker};
pub fn do_batch_format<TEnvironment: Environment, F>(
environment: &TEnvironment,
error_logger: &ErrorCountLogger<TEnvironment>,
plugin_pools: &Arc<PluginPools<TEnvironment>>,
file_paths_by_plugin: HashMap<String, Vec<PathBuf>>,
action: F
) -> Result<(), ErrBox> where F: Fn(&InitializedPluginPool<TEnvironment>, &Path, &mut Box<dyn InitializedPlugin>) + Send + 'static + Clone {
let registry = Arc::new(WorkerRegistry::new(plugin_pools.clone(), file_paths_by_plugin));
let long_format_checker_thread = LongFormatCheckerThread::new(environment, registry.clone());
let thread_handles = registry.workers.iter().skip(1).map(|worker| {
let worker = worker.clone();
let error_logger = error_logger.clone();
let action = action.clone();
let registry = registry.clone();
thread::spawn(move || {
run_thread(&error_logger, registry, &worker, action)
})
}).collect::<Vec<_>>();
long_format_checker_thread.spawn();
let first_worker = registry.workers.first().unwrap().clone();
run_thread(error_logger, registry, &first_worker, action);
for handle in thread_handles {
if let Err(_) = handle.join() {
long_format_checker_thread.signal_exit();
return err!(
"A panic occurred. You may want to run in verbose mode (--verbose) to help figure out where it failed then report this as a bug.",
);
}
}
long_format_checker_thread.signal_exit();
return Ok(());
}
fn run_thread<TEnvironment: Environment, F>(
error_logger: &ErrorCountLogger<TEnvironment>,
registry: Arc<WorkerRegistry<TEnvironment>>,
worker: &Worker<TEnvironment>,
action: F,
) where F: Fn(&InitializedPluginPool<TEnvironment>, &Path, &mut Box<dyn InitializedPlugin>) + Send + 'static + Clone {
let mut current_plugin: Option<(Box<dyn InitializedPlugin>, Arc<InitializedPluginPool<TEnvironment>>)> = None;
loop {
if let Err(err) = do_local_work(error_logger, ®istry, &worker, action.clone(), current_plugin.take()) {
error_logger.log_error(&err.to_string());
return;
}
if let Some(stolen_work) = registry.steal_work(worker.id) {
if let Some(plugin) = stolen_work.plugin {
current_plugin = Some((plugin, stolen_work.work.pool.clone()));
}
worker.add_work(stolen_work.work);
} else {
return; }
}
}
fn do_local_work<TEnvironment: Environment, F>(
error_logger: &ErrorCountLogger<TEnvironment>,
registry: &WorkerRegistry<TEnvironment>,
worker: &Worker<TEnvironment>,
action: F,
current_plugin: Option<(Box<dyn InitializedPlugin>, Arc<InitializedPluginPool<TEnvironment>>)>,
) -> Result<(), ErrBox> where F: Fn(&InitializedPluginPool<TEnvironment>, &Path, &mut Box<dyn InitializedPlugin>) + Send + 'static + Clone {
let mut current_plugin = current_plugin;
loop {
let (pool, file_path) = if let Some(next_work) = worker.take_next_work() {
next_work
} else {
release_current_plugin(&mut current_plugin, registry, worker);
return Ok(()); };
if let Some((_, current_pool)) = current_plugin.as_ref() {
if current_pool.name() != pool.name() {
release_current_plugin(&mut current_plugin, registry, worker);
}
}
if current_plugin.is_none() {
match pool.take_or_create_checking_config_diagnostics(error_logger)? {
TakePluginResult::Success(plugin) => {
current_plugin = Some((plugin, pool));
}
TakePluginResult::HadDiagnostics => {
worker.clear_work_for_current_plugin();
continue;
}
}
}
let plugin_and_pool = current_plugin.as_mut().unwrap();
action(&plugin_and_pool.1, &file_path, &mut plugin_and_pool.0);
}
fn release_current_plugin<TEnvironment: Environment>(
current_plugin: &mut Option<(Box<dyn InitializedPlugin>, Arc<InitializedPluginPool<TEnvironment>>)>,
registry: &WorkerRegistry<TEnvironment>,
worker: &Worker<TEnvironment>,
) {
if let Some((current_plugin, pool)) = current_plugin.take() {
pool.release(current_plugin);
registry.release_pool_if_no_work_in_registry(worker.id, pool.name());
}
}
}