use anyhow::Result;
use dprint_core::configuration::ConfigKeyMap;
use dprint_core::configuration::ConfigurationDiagnostic;
use dprint_core::configuration::GlobalConfiguration;
use dprint_core::plugins::PluginInfo;
use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc;
use crate::configuration::RawPluginConfig;
use crate::environment::Environment;
use crate::plugins::InitializedPlugin;
use crate::plugins::Plugin;
use crate::plugins::PluginPools;
use super::super::format_with_plugin_pool;
use super::InitializedProcessPluginCommunicator;
static PLUGIN_FILE_INITIALIZE: std::sync::Once = std::sync::Once::new();
pub fn get_test_safe_executable_path(executable_file_path: PathBuf, environment: &impl Environment) -> PathBuf {
if environment.is_real() {
executable_file_path
} else {
if cfg!(target_os = "windows") {
let tmp_dir = PathBuf::from("temp");
let temp_process_plugin_file = tmp_dir.join(if cfg!(target_os = "windows") { "temp-plugin.exe" } else { "temp-plugin" });
PLUGIN_FILE_INITIALIZE.call_once(|| {
let _ = std::fs::create_dir(&tmp_dir);
let _ = std::fs::write(&temp_process_plugin_file, environment.read_file_bytes(&executable_file_path).unwrap());
});
temp_process_plugin_file
} else {
PathBuf::from("../../target/release/test-process-plugin")
}
}
}
pub struct ProcessPlugin<TEnvironment: Environment> {
environment: TEnvironment,
executable_file_path: PathBuf,
plugin_info: PluginInfo,
config: Option<(RawPluginConfig, GlobalConfiguration)>,
plugin_pools: Arc<PluginPools<TEnvironment>>,
}
impl<TEnvironment: Environment> ProcessPlugin<TEnvironment> {
pub fn new(environment: TEnvironment, executable_file_path: PathBuf, plugin_info: PluginInfo, plugin_pools: Arc<PluginPools<TEnvironment>>) -> Self {
ProcessPlugin {
environment,
executable_file_path,
plugin_info,
config: None,
plugin_pools,
}
}
}
impl<TEnvironment: Environment> Plugin for ProcessPlugin<TEnvironment> {
fn name(&self) -> &str {
&self.plugin_info.name
}
fn version(&self) -> &str {
&self.plugin_info.version
}
fn config_key(&self) -> &str {
&self.plugin_info.config_key
}
fn file_extensions(&self) -> &Vec<String> {
&self.plugin_info.file_extensions
}
fn file_names(&self) -> &Vec<String> {
&self.plugin_info.file_names
}
fn help_url(&self) -> &str {
&self.plugin_info.help_url
}
fn config_schema_url(&self) -> &str {
&self.plugin_info.config_schema_url
}
fn set_config(&mut self, plugin_config: RawPluginConfig, global_config: GlobalConfiguration) {
self.config = Some((plugin_config, global_config));
}
fn get_config(&self) -> &(RawPluginConfig, GlobalConfiguration) {
self.config.as_ref().expect("Call set_config first.")
}
fn initialize(&self) -> Result<Box<dyn InitializedPlugin>> {
let config = self.config.as_ref().expect("Call set_config first.");
let communicator = InitializedProcessPluginCommunicator::new(
self.environment.clone(),
self.plugin_info.name.clone(),
self.executable_file_path.clone(),
(config.0.properties.clone(), config.1.clone()),
)?;
let process_plugin = InitializedProcessPlugin::new(self.name().to_string(), self.environment.clone(), communicator, self.plugin_pools.clone())?;
Ok(Box::new(process_plugin))
}
}
pub struct InitializedProcessPlugin<TEnvironment: Environment> {
name: String,
environment: TEnvironment,
communicator: InitializedProcessPluginCommunicator<TEnvironment>,
plugin_pools: Arc<PluginPools<TEnvironment>>,
}
impl<TEnvironment: Environment> InitializedProcessPlugin<TEnvironment> {
pub fn new(
name: String,
environment: TEnvironment,
communicator: InitializedProcessPluginCommunicator<TEnvironment>,
plugin_pools: Arc<PluginPools<TEnvironment>>,
) -> Result<Self> {
let initialized_plugin = InitializedProcessPlugin {
name,
environment,
communicator,
plugin_pools,
};
Ok(initialized_plugin)
}
fn inner_format_text(&self, file_path: &Path, file_text: &str, override_config: &ConfigKeyMap) -> Result<String> {
self
.communicator
.format_text(file_path, file_text, override_config, |file_path, file_text, override_config| {
format_with_plugin_pool(&self.name, &file_path, &file_text, &override_config, &self.plugin_pools)
})
}
}
impl<TEnvironment: Environment> InitializedPlugin for InitializedProcessPlugin<TEnvironment> {
fn get_license_text(&self) -> Result<String> {
self.communicator.get_license_text()
}
fn get_resolved_config(&self) -> Result<String> {
self.communicator.get_resolved_config()
}
fn get_config_diagnostics(&self) -> Result<Vec<ConfigurationDiagnostic>> {
self.communicator.get_config_diagnostics()
}
fn format_text(&mut self, file_path: &Path, file_text: &str, override_config: &ConfigKeyMap) -> Result<String> {
let result = self.inner_format_text(file_path, file_text, override_config);
match result {
Ok(result) => Ok(result),
Err(original_err) => {
let process_recreated = match self.communicator.recreate_process_if_dead() {
Ok(process_recreated) => process_recreated,
Err(err) => {
self
.environment
.log_stderr(&format!("Failed to recreate child process plugin after it was unresponsive: {}", err,));
return Err(original_err);
}
};
if process_recreated {
self.inner_format_text(file_path, file_text, override_config)
} else {
Err(original_err)
}
}
}
}
}