use std::{fmt, io, path::Path};
mod input;
pub mod install;
mod output;
pub mod run;
mod run_log;
pub mod status;
pub mod tool_definitions;
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum LaunchType {
PythonLib,
PythonBasedApp,
CondaBasedApp,
Executable,
}
impl fmt::Display for LaunchType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
Self::PythonLib => "Python library",
Self::PythonBasedApp => "Python app (uv)",
Self::CondaBasedApp => "Python app (conda)",
Self::Executable => "Executable",
};
write!(f, "{}", s)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ToolCategory {
Cheminformatics,
StructurePrediction,
ProteinDesign,
PeptideBinderDesign,
MoleculeDynamics,
QuantumChemistry,
AntibodyDesign,
SequencePrediction,
SequenceAnalysis,
PropertyPrediction,
BindingData,
Placeholder,
}
impl fmt::Display for ToolCategory {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
Self::Cheminformatics => "Cheminformatics",
Self::StructurePrediction => "Structure prediction",
Self::ProteinDesign => "Protein Design",
Self::PeptideBinderDesign => "Binder design",
Self::MoleculeDynamics => "Molecular simulation",
Self::QuantumChemistry => "Quantum chemistry",
Self::AntibodyDesign => "Antibody design",
Self::SequencePrediction => "Sequence prediction",
Self::SequenceAnalysis => "Sequence analysis",
Self::PropertyPrediction => "Property prediction",
Self::BindingData => "Binding data",
Self::Placeholder => "Uncategorized",
};
write!(f, "{}", s)
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum OperatingSystem {
Linux,
Windows,
Mac,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ProcessExpense {
Cheap,
Moderate,
Expensive,
}
impl fmt::Display for ProcessExpense {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
Self::Cheap => "Cheap (ms)",
Self::Moderate => "Moderate (s)",
Self::Expensive => "Expensive (min or hours)",
};
write!(f, "{}", s)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum License {
Mit,
ApacheV2,
Bsd3Clause,
Lgpl21OrLater,
PublicDomain,
Other,
}
impl fmt::Display for License {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::Mit => "MIT",
Self::ApacheV2 => "Apache-2.0",
Self::Bsd3Clause => "BSD-3-Clause",
Self::Lgpl21OrLater => "LGPL-2.1-or-later",
Self::PublicDomain => "Public domain",
Self::Other => "Other",
})
}
}
impl License {
pub fn category(self) -> LicenseCategory {
use License::*;
match self {
Mit | ApacheV2 | Bsd3Clause | PublicDomain => LicenseCategory::Permissive,
Lgpl21OrLater => LicenseCategory::Copyleft,
Other => LicenseCategory::Proprietary,
}
}
pub const fn official_url(self) -> Option<&'static str> {
match self {
Self::Mit => Some("https://opensource.org/license/mit"),
Self::ApacheV2 => Some("https://www.apache.org/licenses/LICENSE-2.0"),
Self::Bsd3Clause => Some("https://opensource.org/license/bsd-3-clause"),
Self::Lgpl21OrLater => Some("https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html"),
Self::PublicDomain => None,
Self::Other => None,
}
}
}
#[derive(Clone, Debug)]
pub struct LicenseData {
pub license: License,
pub details: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum LicenseCategory {
Permissive,
Copyleft,
NonCommercial,
Proprietary,
}
impl fmt::Display for LicenseCategory {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
Self::Permissive => "Permissive",
Self::Copyleft => "Copyleft",
Self::NonCommercial => "Non-commercial",
Self::Proprietary => "Proprietary",
};
write!(f, "{}", s)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct SpecData<S> {
pub summary: S,
pub description: S,
pub availability: S,
pub license_details: S,
pub repo_url: Option<S>,
pub home_url: Option<S>,
pub docs_url: Option<S>,
pub paper_url: Option<S>,
pub license: License,
pub license_url: Option<S>,
}
impl SpecData<&'static str> {
pub fn to_owned_data(&self) -> SpecData<String> {
SpecData {
summary: self.summary.to_owned(),
description: self.description.to_owned(),
availability: self.availability.to_owned(),
license_details: self.license_details.to_owned(),
repo_url: self.repo_url.map(str::to_owned),
home_url: self.home_url.map(str::to_owned),
docs_url: self.docs_url.map(str::to_owned),
paper_url: self.paper_url.map(str::to_owned),
license: self.license,
license_url: self.license_url.map(str::to_owned),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Spec {
pub slug: String,
pub data: SpecData<String>,
}
impl Spec {
#[allow(clippy::too_many_arguments)]
pub fn new(
slug: impl Into<String>,
summary: impl Into<String>,
description: impl Into<String>,
availability: impl Into<String>,
license_details: impl Into<String>,
repo_url: Option<String>,
home_url: Option<String>,
docs_url: Option<String>,
paper_url: Option<String>,
license: License,
license_url: Option<String>,
) -> Self {
Self {
slug: slug.into(),
data: SpecData {
summary: summary.into(),
description: description.into(),
availability: availability.into(),
license_details: license_details.into(),
repo_url,
home_url,
docs_url,
paper_url,
license,
license_url,
},
}
}
pub fn links(&self) -> Vec<(&'static str, &str)> {
[
("Documentation", self.data.docs_url.as_deref()),
("Home page", self.data.home_url.as_deref()),
("Paper", self.data.paper_url.as_deref()),
("Source code", self.data.repo_url.as_deref()),
(
"License",
self.data
.license
.official_url()
.or(self.data.license_url.as_deref()),
),
]
.into_iter()
.filter_map(|(label, url)| url.map(|url| (label, url)))
.collect()
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Process {
pub name: String,
pub id: u32,
pub categories: Vec<ToolCategory>,
pub launch_type: LaunchType,
pub license_type: LicenseCategory,
pub expense: ProcessExpense,
pub top_choice: bool,
pub spec: Spec,
}
impl Process {
#[allow(clippy::too_many_arguments)]
pub fn new(
name: impl Into<String>,
id: u32,
categories: Vec<ToolCategory>,
launch_type: LaunchType,
license_type: LicenseCategory,
expense: ProcessExpense,
top_choice: bool,
spec: Spec,
) -> Self {
Self {
name: name.into(),
id,
categories,
launch_type,
license_type,
expense,
top_choice,
spec,
}
}
pub fn install(&self, tools_path: &Path) -> io::Result<()> {
install::Installer::from_environment(tools_path)
.map_err(io::Error::other)?
.install(self.tool()?)
.map_err(io::Error::other)
}
pub fn uninstall(&self, tools_path: &Path) -> io::Result<install::UninstallReport> {
install::Installer::from_environment(tools_path)
.map_err(io::Error::other)?
.uninstall(self.tool()?)
.map_err(io::Error::other)
}
fn tool(&self) -> io::Result<tool_definitions::Tool> {
self.spec
.slug
.parse::<tool_definitions::Tool>()
.or_else(|_| self.name.parse::<tool_definitions::Tool>())
.map_err(io::Error::other)
}
}