use std::collections::HashSet;
use std::sync::Arc;
use axum::extract::{Path, Query, State};
use axum::Json;
use dihardts_omicstools::proteomics::proteases::functions::{
get_by_name as get_protease_by_name, ALL as AVAILABLE_PROTEASES,
};
use fallible_iterator::FallibleIterator;
use futures::TryStreamExt;
use http::StatusCode;
use rustyms::CompoundPeptidoformIon;
use scylla::value::CqlValue;
use serde::Deserialize;
use serde_json::{json, Value as JsonValue};
use crate::database::scylla::peptide_table::PeptideTable;
use crate::entities::peptide::Peptide;
use crate::mass::convert::to_float as mass_to_float;
use crate::mass::convert::to_int as mass_to_int;
use crate::tools::omicstools::convert_to_internal_dummy_peptide;
use crate::tools::peptide_partitioner::get_mass_partition;
use crate::web::web_error::WebError;
use super::app_state::AppState;
#[derive(Deserialize)]
pub struct DigestionRequestBody {
sequence: String,
#[serde(default = "bool::default")]
db_match: bool,
protease: Option<String>,
max_number_of_missed_cleavages: Option<usize>,
min_peptide_length: Option<usize>,
max_peptide_length: Option<usize>,
}
pub async fn digest(
State(app_state): State<Arc<AppState>>,
Json(payload): Json<DigestionRequestBody>,
) -> Result<Json<JsonValue>, WebError> {
let configuration = app_state.get_configuration_as_ref();
let protease = get_protease_by_name(
match &payload.protease {
Some(protease) => protease,
None => configuration.get_protease_name(),
},
match &payload.min_peptide_length {
Some(min_len) => Some(*min_len),
None => configuration.get_min_peptide_length(),
},
match &payload.max_peptide_length {
Some(max_len) => Some(*max_len),
None => configuration.get_max_peptide_length(),
},
match &payload.max_number_of_missed_cleavages {
Some(max_missed_cleavages) => Some(*max_missed_cleavages),
None => configuration.get_max_number_of_missed_cleavages(),
},
)?;
let peptides: HashSet<Peptide> = convert_to_internal_dummy_peptide(
Box::new(protease.cleave(&payload.sequence)?),
app_state.get_configuration_as_ref().get_partition_limits(),
)
.collect()?;
if payload.db_match {
let mut select_params_by_partition: Vec<Vec<(CqlValue, CqlValue)>> =
vec![Vec::new(); configuration.get_partition_limits().len()];
for peptide in peptides.iter() {
select_params_by_partition[peptide.get_partition() as usize].push((
CqlValue::BigInt(peptide.get_mass()),
CqlValue::Text(peptide.get_sequence().to_owned()),
));
}
let mut db_peptides: Vec<Peptide> = Vec::with_capacity(peptides.len());
for (partition, select_params) in select_params_by_partition.iter().enumerate() {
if select_params.is_empty() {
continue;
}
let mut statement_addition = "WHERE partition = ? AND (mass, sequence) IN (".to_owned();
statement_addition.push_str(
&(0..select_params.len())
.map(|_| "(?, ?)".to_owned())
.collect::<Vec<String>>()
.join(", "),
);
statement_addition.push(')');
let partition = CqlValue::BigInt(partition as i64);
let mut select_params_ref: Vec<&CqlValue> =
Vec::with_capacity(select_params.len() * 2 + 1);
select_params_ref.push(&partition);
select_params_ref.extend(
select_params
.iter()
.flat_map(|params| vec![¶ms.0, ¶ms.1]),
);
let db_peptides_partition = PeptideTable::select(
app_state.get_db_client_as_ref(),
&statement_addition,
select_params_ref.as_slice(),
)
.await?
.try_collect::<Vec<_>>()
.await?;
db_peptides.extend(db_peptides_partition);
}
Ok(Json(json!({
"peptides": peptides,
"db_peptides": db_peptides,
})))
} else {
Ok(Json(json!({
"peptides": peptides,
})))
}
}
pub async fn get_mass(Path(sequence): Path<String>) -> Result<Json<JsonValue>, WebError> {
let peptide = CompoundPeptidoformIon::pro_forma(&sequence, None)
.map_err(|err| WebError::new(StatusCode::UNPROCESSABLE_ENTITY, format!("{}", err)))?;
let mass = match peptide.formulas().mass_bounds().into_option() {
Some((min, _)) => min.monoisotopic_mass().value,
None => {
return Err(WebError::new(
StatusCode::UNPROCESSABLE_ENTITY,
"Could not calculate mass for the given sequence".to_string(),
));
}
};
Ok(Json(json!({
"mass": mass,
})))
}
pub async fn get_proteases() -> Result<Json<JsonValue>, WebError> {
let mut protease_names: Vec<&str> = Vec::from(AVAILABLE_PROTEASES);
protease_names.sort();
Ok(Json(json!(protease_names)))
}
#[derive(Deserialize)]
pub struct GetPartitionQuery {
mass: f64,
}
pub async fn get_partition(
State(app_state): State<Arc<AppState>>,
Query(query_payload): Query<GetPartitionQuery>,
) -> Result<Json<JsonValue>, WebError> {
let partition_limits = app_state.get_configuration_as_ref().get_partition_limits();
let partition_index = get_mass_partition(partition_limits, mass_to_int(query_payload.mass))?;
Ok(Json(json!({
"partition": partition_index,
"partition_limit": mass_to_float(partition_limits[partition_index]),
})))
}