use std::fmt::{ Display, Formatter, Result as FmtResult };
use std::str::FromStr;
use std::borrow::Cow;
use serde::{
ser::{ Serialize, Serializer },
de::{ Deserialize, Deserializer, Visitor, Error as DeError },
};
use serde_json::{ self, Value };
use super::Request;
use crate::{
settings::Settings,
job::{ JobId, JobStatus },
error::Error,
};
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct RetrieveResult {
pub job_id: JobId,
}
impl Request for RetrieveResult {
type Body = ();
type Response = RetrieveResultResponse;
fn endpoint(&self) -> Cow<str> {
format!("/results/{}?engine=d3", self.job_id).into()
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RetrieveResultResponse {
#[serde(rename = "_id")]
pub job_id: JobId,
pub status: JobStatus,
#[serde(default, rename = "pdbName", skip_serializing_if = "Option::is_none")]
pub pdb_id: Option<String>,
#[serde(default, rename = "fileName", skip_serializing_if = "Option::is_none")]
pub file_name: Option<String>,
#[serde(flatten, default)]
pub settings: Settings,
pub nodes: Vec<Node>,
pub edges: Vec<Edge>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Node {
#[serde(rename = "NodeId")]
pub node_id: NodeId,
#[serde(rename = "Chain")]
pub chain_id: char,
#[serde(rename = "Position")]
pub position: isize,
#[serde(rename = "Residue")]
pub residue: Residue,
pub x: f64,
pub y: f64,
pub z: f64,
#[serde(rename = "Dssp")]
pub dssp_structure: DsspStructure,
#[serde(rename = "Degree")]
pub degree: usize,
#[serde(rename = "Accessibility")]
pub accessibility: f64,
#[serde(rename = "Bfactor_CA")]
pub bfactor_ca: f64,
#[serde(rename = "Tap", default, skip_serializing_if = "Option::is_none")]
pub tap_energy: Option<f64>,
#[serde(rename = "Rapdf", default, skip_serializing_if = "Option::is_none")]
pub rapdf_energy: Option<f64>,
#[serde(rename = "pdbFileName")]
pub pdb_file_name: String,
#[serde(rename = "Entropy", default, skip_serializing_if = "Option::is_none")]
pub entropy: Option<f64>,
#[serde(rename = "MIcomulative", default, skip_serializing_if = "Option::is_none")]
pub cumul_mutual_entropy: Option<f64>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct NodeId {
pub chain_id: char,
pub position: isize,
pub insertion_code: char,
pub residue: Residue,
}
impl Display for NodeId {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
write!(
f, "{}:{}:{}:{}",
self.chain_id,
self.position,
self.insertion_code,
self.residue,
)
}
}
impl FromStr for NodeId {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let v: Vec<_> = s.split(':').collect();
if v.len() == 4 {
Ok(NodeId {
chain_id: v[0].parse()?,
position: v[1].parse()?,
insertion_code: v[2].parse()?,
residue: v[3].parse()?,
})
} else {
Err(Error::Serialization(String::from(
"Node ID format must be chain:position:insertion:residue"
)))
}
}
}
impl Serialize for NodeId {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.collect_str(self)
}
}
impl<'a> Deserialize<'a> for NodeId {
fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_str(NodeIdVisitor)
}
}
#[derive(Debug, Clone, Copy, Default)]
struct NodeIdVisitor;
impl<'a> Visitor<'a> for NodeIdVisitor {
type Value = NodeId;
fn expecting(&self, f: &mut Formatter) -> FmtResult {
f.write_str("a string representation of a node ID")
}
fn visit_str<E: DeError>(self, s: &str) -> Result<Self::Value, E> {
NodeId::from_str(s).map_err(E::custom)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub enum Residue {
#[serde(rename = "ALA")]
Alanine,
#[serde(rename = "ARG")]
Arginine,
#[serde(rename = "ASN")]
Asparagine,
#[serde(rename = "ASP")]
AsparticAcid,
#[serde(rename = "CYS")]
Cysteine,
#[serde(rename = "GLU")]
GlutamicAcid,
#[serde(rename = "GLN")]
Glutamine,
#[serde(rename = "GLY")]
Glycine,
#[serde(rename = "HCY")]
Homocysteine,
#[serde(rename = "HIS")]
Histidine,
#[serde(rename = "HSE")]
Homoserine,
#[serde(rename = "ILE")]
Isoleucine,
#[serde(rename = "LEU")]
Leucine,
#[serde(rename = "LYS")]
Lysine,
#[serde(rename = "MET")]
Methionine,
#[serde(rename = "NLE")]
Norleucine,
#[serde(rename = "NVA")]
Norvaline,
#[serde(rename = "ORN")]
Ornithine,
#[serde(rename = "PEN")]
Penicillamine,
#[serde(rename = "PHE")]
Phenylalanine,
#[serde(rename = "PRO")]
Proline,
#[serde(rename = "PYL")]
Pyrrolysine,
#[serde(rename = "SEC")]
Selenocysteine,
#[serde(rename = "SER")]
Serine,
#[serde(rename = "THR")]
Threonine,
#[serde(rename = "TRP")]
Tryptophan,
#[serde(rename = "TYR")]
Tyrosine,
#[serde(rename = "VAL")]
Valine,
#[serde(rename = "ASX")]
AsparagineOrAsparticAcid,
#[serde(rename = "GLX")]
GlutamineOrGlutamicAcid,
#[serde(rename = "XLE")]
LeucineOrIsoleucine,
#[serde(rename = "XAA")]
Unknown,
}
impl Display for Residue {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
match serde_json::to_value(self) {
Ok(Value::String(ref s)) => f.pad(s),
_ => panic!("Residue didn't serialize to a string"),
}
}
}
impl FromStr for Residue {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
serde_json::from_value(Value::from(s)).map_err(From::from)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum DsspStructure {
#[serde(rename = " ")]
None,
#[serde(rename = "G")]
Helix310,
#[serde(rename = "H")]
HelixAlpha,
#[serde(rename = "I")]
HelixPi,
#[serde(rename = "T")]
TurnHBond,
#[serde(rename = "E")]
BetaExtended,
#[serde(rename = "B")]
BetaIsolated,
#[serde(rename = "S")]
Bend,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Edge {
#[serde(rename = "NodeId1")]
pub node_id_1: NodeId,
#[serde(rename = "NodeId2")]
pub node_id_2: NodeId,
#[serde(rename = "Interaction")]
pub interaction: Interaction,
#[serde(rename = "Atom1")]
pub atom_1: Atom,
#[serde(rename = "Atom2")]
pub atom_2: Atom,
#[serde(rename = "Distance")]
pub distance: f64,
#[serde(rename = "Angle", with = "serde_angle")]
pub angle: Option<f64>,
#[serde(rename = "Energy")]
pub energy: f64,
#[serde(
rename = "Donor", default,
deserialize_with = "deserialize_empty_nodeid",
skip_serializing_if = "Option::is_none",
)]
pub donor: Option<NodeId>,
#[serde(
rename = "Positive", default,
deserialize_with = "deserialize_empty_nodeid",
skip_serializing_if = "Option::is_none",
)]
pub positive: Option<NodeId>,
#[serde(
rename = "Cation", default,
deserialize_with = "deserialize_empty_nodeid",
skip_serializing_if = "Option::is_none",
)]
pub cation: Option<NodeId>,
#[serde(rename = "MI", default, skip_serializing_if = "Option::is_none")]
pub mutual_inf: Option<f64>,
#[serde(rename = "APC", default, skip_serializing_if = "Option::is_none")]
pub apc: Option<f64>,
#[serde(rename = "MIcorrected", default, skip_serializing_if = "Option::is_none")]
pub corrected_mi: Option<f64>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Interaction {
pub main_type: InteractionMainType,
pub subtype_1: InteractionSubType,
pub subtype_2: InteractionSubType,
}
impl Display for Interaction {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
write!(f, "{}:{}_{}", self.main_type, self.subtype_1, self.subtype_2)
}
}
impl FromStr for Interaction {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let parts: Vec<_> = s.split(':').collect();
if parts.len() == 2 {
let main = parts[0];
let parts: Vec<_> = parts[1].split('_').collect();
if parts.len() == 2 {
Ok(Interaction {
main_type: main.parse()?,
subtype_1: parts[0].parse()?,
subtype_2: parts[1].parse()?,
})
} else {
Err(Error::Serialization(String::from(
"Interaction type format must be main:sub1_sub2"
)))
}
} else {
Err(Error::Serialization(String::from(
"Interaction type format must be main:sub1_sub2"
)))
}
}
}
impl Serialize for Interaction {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.collect_str(self)
}
}
impl<'a> Deserialize<'a> for Interaction {
fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_str(InteractionVisitor)
}
}
#[derive(Debug, Clone, Copy, Default)]
struct InteractionVisitor;
impl<'a> Visitor<'a> for InteractionVisitor {
type Value = Interaction;
fn expecting(&self, f: &mut Formatter) -> FmtResult {
f.write_str("an interaction specification string")
}
fn visit_str<E: DeError>(self, s: &str) -> Result<Self::Value, E> {
Interaction::from_str(s).map_err(E::custom)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum InteractionMainType {
#[serde(rename = "HBOND")]
HydrogenBond,
#[serde(rename = "VDW")]
VanDerWaals,
#[serde(rename = "SSBOND")]
Disulphide,
#[serde(rename = "IONIC")]
Ionic,
#[serde(rename = "PIPISTACK")]
PiPiStack,
#[serde(rename = "PICATION")]
PiCation,
}
impl Display for InteractionMainType {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
match serde_json::to_value(self) {
Ok(Value::String(ref s)) => f.pad(s),
_ => panic!("InteractionMainType didn't serialize to a string"),
}
}
}
impl FromStr for InteractionMainType {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
serde_json::from_value(Value::from(s)).map_err(From::from)
}
}
impl Display for InteractionSubType {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
match serde_json::to_value(self) {
Ok(Value::String(ref s)) => f.pad(s),
_ => panic!("InteractionSubType didn't serialize to a string"),
}
}
}
impl FromStr for InteractionSubType {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
serde_json::from_value(Value::from(s)).map_err(From::from)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum InteractionSubType {
#[serde(rename = "MC")]
MainChain,
#[serde(rename = "SC")]
SideChain,
#[serde(rename = "LIG")]
Ligand,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Atom {
Name(String),
Coords {
x: f64,
y: f64,
z: f64,
},
}
impl Display for Atom {
fn fmt(&self, f: &mut Formatter) -> FmtResult {
match *self {
Atom::Name(ref name) => f.pad(name),
Atom::Coords { x, y, z } => write!(f, "{},{},{}", x, y, z),
}
}
}
impl FromStr for Atom {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let parts: Vec<_> = s.split(',').collect();
if parts.len() == 3 {
Ok(Atom::Coords {
x: parts[0].parse()?,
y: parts[1].parse()?,
z: parts[2].parse()?,
})
} else {
Ok(Atom::Name(s.into()))
}
}
}
impl Serialize for Atom {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.collect_str(self)
}
}
impl<'a> Deserialize<'a> for Atom {
fn deserialize<D: Deserializer<'a>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_str(AtomVisitor)
}
}
#[derive(Debug, Clone, Copy, Default)]
struct AtomVisitor;
impl<'a> Visitor<'a> for AtomVisitor {
type Value = Atom;
fn expecting(&self, f: &mut Formatter) -> FmtResult {
f.write_str("an atom name or comma-separated X, Y, Z coordinates")
}
fn visit_str<E: DeError>(self, s: &str) -> Result<Self::Value, E> {
Atom::from_str(s).map_err(E::custom)
}
}
mod serde_angle {
use serde::{
ser::Serializer,
de::{ Deserializer, Visitor, Error },
};
use std::fmt::{ Formatter, Result as FmtResult };
pub fn serialize<S: Serializer>(value: &Option<f64>, s: S) -> Result<S::Ok, S::Error> {
s.serialize_f64(value.unwrap_or(-999.9))
}
pub fn deserialize<'a, D: Deserializer<'a>>(d: D) -> Result<Option<f64>, D::Error> {
d.deserialize_f64(AngleVisitor)
}
#[derive(Debug, Clone, Copy, Default)]
struct AngleVisitor;
impl<'a> Visitor<'a> for AngleVisitor {
type Value = Option<f64>;
fn expecting(&self, f: &mut Formatter) -> FmtResult {
f.write_str("a number representing an angle")
}
#[allow(clippy::float_cmp)]
fn visit_f64<E: Error>(self, v: f64) -> Result<Self::Value, E> {
if v == -999.9 {
Ok(None)
} else {
Ok(Some(v))
}
}
#[allow(clippy::cast_precision_loss)]
fn visit_i64<E: Error>(self, v: i64) -> Result<Self::Value, E> {
Ok(Some(v as f64))
}
#[allow(clippy::cast_precision_loss)]
fn visit_u64<E: Error>(self, v: u64) -> Result<Self::Value, E> {
Ok(Some(v as f64))
}
}
}
fn deserialize_empty_nodeid<'a, D: Deserializer<'a>>(d: D) -> Result<Option<NodeId>, D::Error> {
d.deserialize_str(EmptyNodeIdVisitor)
}
#[derive(Debug, Clone, Copy, Default)]
struct EmptyNodeIdVisitor;
impl<'a> Visitor<'a> for EmptyNodeIdVisitor {
type Value = Option<NodeId>;
fn expecting(&self, f: &mut Formatter) -> FmtResult {
f.write_str("a NodeId or an empty string")
}
fn visit_str<E: DeError>(self, s: &str) -> Result<Self::Value, E> {
if s.is_empty() {
Ok(None)
} else {
NodeId::from_str(s).map(Some).map_err(E::custom)
}
}
}