use anyhow::Result;
use dprint_core::async_runtime::async_trait;
use dprint_core::configuration::ConfigurationDiagnostic;
use dprint_core::plugins::CheckConfigUpdatesMessage;
use dprint_core::plugins::ConfigChange;
use dprint_core::plugins::FileMatchingInfo;
use dprint_core::plugins::FormatResult;
use dprint_core::plugins::PluginInfo;
use std::path::PathBuf;
use std::rc::Rc;
use std::sync::Arc;
use std::time::Instant;
use crate::environment::Environment;
use crate::plugins::FormatConfig;
use crate::plugins::InitializedPlugin;
use crate::plugins::InitializedPluginFormatRequest;
use crate::plugins::Plugin;
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 {
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" });
let temp_process_plugin_file_0_3_0 = tmp_dir.join(if cfg!(target_os = "windows") {
"temp-plugin-0.3.0.exe"
} else {
"temp-plugin-0.3.0"
});
PLUGIN_FILE_INITIALIZE.call_once(|| {
fn create_for_file(file_path: &PathBuf, bytes: &[u8]) {
#[allow(clippy::disallowed_methods)]
let _ = std::fs::write(file_path, bytes);
if cfg!(unix) {
std::process::Command::new("sh")
.arg("-c")
.arg(format!("chmod +x {}", file_path.to_string_lossy()))
.status()
.unwrap();
}
}
#[allow(clippy::disallowed_methods)]
let _ = std::fs::create_dir(&tmp_dir);
let bytes = environment.read_file_bytes(&executable_file_path).unwrap();
create_for_file(&temp_process_plugin_file, &bytes);
create_for_file(&temp_process_plugin_file_0_3_0, &bytes);
});
if executable_file_path.to_string_lossy().contains("0.1.0") {
temp_process_plugin_file
} else if executable_file_path.to_string_lossy().contains("0.3.0") {
temp_process_plugin_file_0_3_0
} else {
unreachable!();
}
}
}
pub struct ProcessPlugin<TEnvironment: Environment> {
environment: TEnvironment,
executable_file_path: PathBuf,
plugin_info: PluginInfo,
}
impl<TEnvironment: Environment> ProcessPlugin<TEnvironment> {
pub fn new(environment: TEnvironment, executable_file_path: PathBuf, plugin_info: PluginInfo) -> Self {
ProcessPlugin {
environment,
executable_file_path,
plugin_info,
}
}
}
#[async_trait(?Send)]
impl<TEnvironment: Environment> Plugin for ProcessPlugin<TEnvironment> {
fn info(&self) -> &PluginInfo {
&self.plugin_info
}
fn is_process_plugin(&self) -> bool {
true
}
async fn initialize(&self) -> Result<Rc<dyn InitializedPlugin>> {
let start_instant = Instant::now();
let plugin_name = &self.info().name;
log_debug!(self.environment, "Creating instance of {}", plugin_name);
let communicator = InitializedProcessPluginCommunicator::new(plugin_name.to_string(), self.executable_file_path.clone(), self.environment.clone()).await?;
let process_plugin = InitializedProcessPlugin::new(communicator)?;
let result: Rc<dyn InitializedPlugin> = Rc::new(process_plugin);
log_debug!(
self.environment,
"Created instance of {} in {}ms",
plugin_name,
start_instant.elapsed().as_millis() as u64
);
Ok(result)
}
}
pub struct InitializedProcessPlugin<TEnvironment: Environment> {
communicator: Rc<InitializedProcessPluginCommunicator<TEnvironment>>,
}
impl<TEnvironment: Environment> InitializedProcessPlugin<TEnvironment> {
pub fn new(communicator: InitializedProcessPluginCommunicator<TEnvironment>) -> Result<Self> {
Ok(Self {
communicator: Rc::new(communicator),
})
}
}
#[async_trait(?Send)]
impl<TEnvironment: Environment> InitializedPlugin for InitializedProcessPlugin<TEnvironment> {
async fn license_text(&self) -> Result<String> {
self.communicator.get_license_text().await
}
async fn resolved_config(&self, config: Arc<FormatConfig>) -> Result<String> {
self.communicator.get_resolved_config(&config).await
}
async fn file_matching_info(&self, config: Arc<FormatConfig>) -> Result<FileMatchingInfo> {
self.communicator.get_file_matching_info(&config).await
}
async fn config_diagnostics(&self, config: Arc<FormatConfig>) -> Result<Vec<ConfigurationDiagnostic>> {
self.communicator.get_config_diagnostics(&config).await
}
async fn check_config_updates(&self, message: CheckConfigUpdatesMessage) -> Result<Vec<ConfigChange>> {
self.communicator.check_config_updates(&message).await
}
async fn format_text(&self, request: InitializedPluginFormatRequest) -> FormatResult {
self.communicator.format_text(request).await
}
async fn shutdown(&self) -> () {
self.communicator.shutdown().await
}
}