use super::CustomError;
use crate::annotate::seqvars::provider::Provider;
use actix_web::{
get,
web::{self, Data, Json, Path},
};
use itertools::Itertools;
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
clap::ValueEnum,
serde::Deserialize,
serde::Serialize,
utoipa::ToSchema,
)]
#[serde(rename_all = "snake_case")]
pub enum Assembly {
Grch37,
Grch38,
}
impl From<biocommons_bioutils::assemblies::Assembly> for Assembly {
fn from(assembly: biocommons_bioutils::assemblies::Assembly) -> Self {
match assembly {
biocommons_bioutils::assemblies::Assembly::Grch37
| biocommons_bioutils::assemblies::Assembly::Grch37p10 => Assembly::Grch37,
biocommons_bioutils::assemblies::Assembly::Grch38 => Assembly::Grch38,
}
}
}
impl TryFrom<&str> for Assembly {
type Error = anyhow::Error;
fn try_from(value: &str) -> Result<Self, Self::Error> {
match value.to_lowercase().as_str() {
"grch37" | "grch37p10" => Ok(Assembly::Grch37),
"grch38" => Ok(Assembly::Grch38),
_ => Err(anyhow::anyhow!("Unsupported assembly: {}", value)),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize, utoipa::ToSchema)]
pub struct SoftwareVersions {
pub mehari: String,
pub hgvs_rs: String,
}
impl SoftwareVersions {
pub fn new() -> Result<Self, anyhow::Error> {
let mehari = crate::built_info::PKG_VERSION.to_string();
let hgvs_rs = crate::built_info::DEPENDENCIES
.iter()
.find(|(name, _)| name == &"hgvs")
.map(|(_, version)| version.to_string())
.ok_or_else(|| anyhow::anyhow!("Failed to find hgvs version"))?;
Ok(Self { mehari, hgvs_rs })
}
}
#[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize, utoipa::ToSchema)]
pub struct DataVersionEntry {
pub assembly: String,
pub version_refseq: Option<String>,
pub version_ensembl: Option<String>,
pub version_annotation: String,
pub annotation_name: String,
}
impl DataVersionEntry {
pub fn from_provider(provider: &Provider) -> Self {
let assembly = provider.assembly().clone();
let versions = &provider.tx_seq_db.source_version;
let version_for = |source_name: &str| {
let version = versions
.iter()
.filter(|&v| v.source_name == source_name)
.map(|v| v.source_version.clone())
.collect::<Vec<_>>()
.join(",");
(!version.is_empty()).then_some(version)
};
let version_refseq = version_for("refseq");
let version_ensembl = version_for("ensembl");
let version_annotation = versions
.iter()
.map(|v| v.annotation_version.clone())
.join(",");
let annotation_name = versions.iter().map(|v| v.annotation_name.clone()).join(",");
Self {
assembly,
version_refseq,
version_ensembl,
version_annotation,
annotation_name,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize, utoipa::ToSchema)]
pub struct VersionsInfoResponse {
pub software: SoftwareVersions,
pub data: Vec<DataVersionEntry>,
}
impl VersionsInfoResponse {
pub fn from_web_server_data(data: &super::WebServerData) -> Result<Self, anyhow::Error> {
let software = SoftwareVersions::new()?;
let data = data
.provider
.values()
.map(|provider| DataVersionEntry::from_provider(provider.as_ref()))
.collect();
Ok(Self { software, data })
}
}
#[allow(clippy::unused_async)]
#[utoipa::path(
get,
operation_id = "versionsInfo",
responses(
(status = 200, description = "Version information.", body = VersionsInfoResponse),
(status = 500, description = "Internal server error.", body = CustomError)
)
)]
#[get("/api/v1/versionsInfo")]
async fn handle(
data: Data<super::WebServerData>,
_path: Path<()>,
_query: web::Query<()>,
) -> actix_web::Result<Json<VersionsInfoResponse>, CustomError> {
Ok(Json(
VersionsInfoResponse::from_web_server_data(data.into_inner().as_ref())
.map_err(|e| CustomError::new(anyhow::anyhow!("Problem determining version: {}", e)))?,
))
}