use actix_web::{
get,
web::{self, Data, Json, Path},
};
use crate::server::run::GeneNames;
use super::error::CustomError;
use serde_with::{formats::CommaSeparator, StringWithSeparator};
#[serde_with::skip_serializing_none]
#[serde_with::serde_as]
#[derive(
serde::Serialize, serde::Deserialize, Debug, Clone, utoipa::ToSchema, utoipa::IntoParams,
)]
#[serde(rename_all = "snake_case")]
pub(crate) struct GenesLookupQuery {
#[serde_as(as = "StringWithSeparator::<CommaSeparator, String>")]
pub q: Vec<String>,
}
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
#[serde_with::skip_serializing_none]
struct Container {
pub genes: indexmap::IndexMap<String, Option<GeneNames>>,
}
async fn handle_impl(
data: Data<crate::server::run::WebServerData>,
_path: Path<()>,
query: web::Query<GenesLookupQuery>,
) -> actix_web::Result<Container, CustomError> {
let genes_db = data.genes.as_ref().ok_or(CustomError::new(anyhow::anyhow!(
"genes database not available"
)))?;
let genes = indexmap::IndexMap::from_iter(query.q.iter().map(|q| {
let v = genes_db
.data
.name_to_hgnc_idx
.get(q)
.map(|idx| genes_db.data.gene_names[*idx].clone());
(q.clone(), v)
}));
Ok(Container {
genes,
})
}
#[get("/genes/lookup")]
async fn handle(
data: Data<crate::server::run::WebServerData>,
path: Path<()>,
query: web::Query<GenesLookupQuery>,
) -> actix_web::Result<Json<Container>, CustomError> {
Ok(Json(handle_impl(data, path, query).await?))
}
#[derive(
Debug, Clone, serde::Serialize, serde::Deserialize, utoipa::ToSchema, utoipa::ToResponse,
)]
pub(crate) struct GenesLookupResultEntry {
pub query: String,
pub gene_names: Option<GeneNames>,
}
#[derive(
Debug, Clone, serde::Serialize, serde::Deserialize, utoipa::ToSchema, utoipa::ToResponse,
)]
pub(crate) struct GenesLookupResponse {
pub genes: Vec<GenesLookupResultEntry>,
}
impl From<Container> for GenesLookupResponse {
fn from(container: Container) -> Self {
Self {
genes: container
.genes
.into_iter()
.map(|(query, gene_names)| GenesLookupResultEntry { query, gene_names })
.collect(),
}
}
}
#[utoipa::path(
get,
operation_id = "genesLookup",
params(GenesLookupQuery),
responses(
(status = 200, description = "Genes search results.", body = GenesLookupResponse),
(status = 500, description = "Internal server error.", body = CustomError)
)
)]
#[get("/api/v1/genes/lookup")]
async fn handle_with_openapi(
data: Data<crate::server::run::WebServerData>,
path: Path<()>,
query: web::Query<GenesLookupQuery>,
) -> actix_web::Result<Json<GenesLookupResponse>, CustomError> {
Ok(Json(handle_impl(data, path, query).await?.into()))
}