use crate::{
common::{
error::{inv_arg, oe_err, Result},
log::{tee_file::TeeFileConfiguration, Loglevel, LoglevelFilter},
types::{ArbCmd, PluginType},
},
host::{
configuration::{
env_mod::EnvMod, plugin::log::PluginLogConfiguration,
stream_capture_mode::StreamCaptureMode, timeout::Timeout, PluginConfiguration,
ReproductionPathStyle,
},
plugin::{process::PluginProcess, Plugin},
reproduction::PluginReproduction,
},
};
use serde::{Deserialize, Serialize};
use std::{
env::{current_exe, split_paths, var_os},
ffi::OsString,
path::PathBuf,
};
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub struct PluginProcessSpecification {
pub executable: PathBuf,
pub script: Option<PathBuf>,
pub typ: PluginType,
}
impl PluginProcessSpecification {
pub fn new<T>(
executable: impl Into<PathBuf>,
script: Option<T>,
typ: impl Into<PluginType>,
) -> PluginProcessSpecification
where
T: Into<PathBuf>,
{
PluginProcessSpecification {
executable: executable.into(),
script: script.map(std::convert::Into::into),
typ: typ.into(),
}
}
pub fn from_sugar(
specification: impl Into<PathBuf>,
typ: PluginType,
) -> Result<PluginProcessSpecification> {
let specification = specification.into();
let sugared = specification.clone();
let mut specification = PluginProcessSpecification {
executable: specification,
script: None,
typ,
};
if specification.executable.exists() {
if specification.executable.extension().is_some() {
specification.script = Some(specification.executable);
specification.executable = specification
.script
.as_ref()
.unwrap()
.extension()
.unwrap()
.into();
} else {
return Ok(specification);
}
} else {
if specification
.executable
.as_os_str()
.to_string_lossy()
.contains('/')
{
return inv_arg(format!(
"the plugin specification '{}' appears to be a path, \
but the referenced file does not exist",
&specification.executable.to_string_lossy()
));
}
}
let mut prefix: OsString = match typ {
PluginType::Frontend => "dqcsfe",
PluginType::Operator => "dqcsop",
PluginType::Backend => "dqcsbe",
}
.into();
prefix.push(specification.executable.as_os_str());
specification.executable = prefix.into();
if specification.executable.exists() {
return Ok(specification);
}
if let Ok(dqcsim_dir) = current_exe() {
let mut exec = dqcsim_dir
.parent()
.ok_or_else(oe_err("Could not determine path to DQCsim binary."))?
.to_path_buf();
exec.push(&specification.executable);
if exec.exists() {
specification.executable = exec;
return Ok(specification);
}
}
if let Some(sys_path) = var_os("PATH") {
for base in split_paths(&sys_path) {
let mut exec = base.clone();
exec.push(&specification.executable);
if exec.exists() {
specification.executable = exec;
return Ok(specification);
}
}
}
inv_arg(format!(
"could not find plugin executable '{}', needed for plugin \
specification '{}'",
specification.executable.to_string_lossy(),
sugared.to_string_lossy(),
))
}
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub struct PluginProcessFunctionalConfiguration {
pub init: Vec<ArbCmd>,
pub env: Vec<EnvMod>,
pub work: PathBuf,
}
impl Default for PluginProcessFunctionalConfiguration {
fn default() -> PluginProcessFunctionalConfiguration {
PluginProcessFunctionalConfiguration {
init: vec![],
env: vec![],
work: ".".into(),
}
}
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub struct PluginProcessNonfunctionalConfiguration {
pub verbosity: LoglevelFilter,
pub tee_files: Vec<TeeFileConfiguration>,
pub stdout_mode: StreamCaptureMode,
pub stderr_mode: StreamCaptureMode,
pub accept_timeout: Timeout,
pub shutdown_timeout: Timeout,
}
impl Default for PluginProcessNonfunctionalConfiguration {
fn default() -> PluginProcessNonfunctionalConfiguration {
PluginProcessNonfunctionalConfiguration {
verbosity: LoglevelFilter::Trace,
tee_files: vec![],
stdout_mode: StreamCaptureMode::Capture(Loglevel::Info),
stderr_mode: StreamCaptureMode::Capture(Loglevel::Info),
accept_timeout: Timeout::from_seconds(5),
shutdown_timeout: Timeout::from_seconds(5),
}
}
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub struct PluginProcessConfiguration {
pub name: String,
pub specification: PluginProcessSpecification,
pub functional: PluginProcessFunctionalConfiguration,
pub nonfunctional: PluginProcessNonfunctionalConfiguration,
}
impl PluginProcessConfiguration {
pub fn new(
name: impl Into<String>,
specification: PluginProcessSpecification,
) -> PluginProcessConfiguration {
PluginProcessConfiguration {
name: name.into(),
specification,
functional: PluginProcessFunctionalConfiguration::default(),
nonfunctional: PluginProcessNonfunctionalConfiguration::default(),
}
}
}
impl Into<Box<dyn PluginConfiguration>> for PluginProcessConfiguration {
fn into(self) -> Box<dyn PluginConfiguration> {
Box::new(self) as Box<dyn PluginConfiguration>
}
}
impl PluginConfiguration for PluginProcessConfiguration {
fn instantiate(self: Box<Self>) -> Box<dyn Plugin> {
Box::new(PluginProcess::new(*self))
}
fn get_log_configuration(&self) -> PluginLogConfiguration {
self.into()
}
fn get_type(&self) -> PluginType {
self.specification.typ
}
fn get_reproduction(&self, path_style: ReproductionPathStyle) -> Result<PluginReproduction> {
Ok(PluginReproduction {
name: self.name.clone(),
executable: path_style.convert_path(&self.specification.executable)?,
script: path_style.convert_path_option(&self.specification.script)?,
functional: PluginProcessFunctionalConfiguration {
init: self.functional.init.clone(),
env: self.functional.env.clone(),
work: path_style.convert_path(&self.functional.work)?,
},
})
}
fn limit_verbosity(&mut self, max_verbosity: LoglevelFilter) {
if self.nonfunctional.verbosity > max_verbosity {
self.nonfunctional.verbosity = max_verbosity;
}
}
fn set_default_name(&mut self, default_name: String) {
if self.name.is_empty() {
self.name = default_name;
}
}
}