#![allow(dead_code)]
use crate::reference_tables;
use crate::structs::*;
use crate::transformation::*;
use std::cmp::Ordering;
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Conformer {
name: String,
alternative_location: Option<String>,
atoms: Vec<Atom>,
modification: Option<(String, String)>,
}
impl Conformer {
pub fn new(name: &str, alt_loc: Option<&str>, atom: Option<Atom>) -> Option<Conformer> {
if let Some(n) = prepare_identifier(name) {
let mut res = Conformer {
name: n,
alternative_location: None,
atoms: Vec::new(),
modification: None,
};
if let Some(al) = alt_loc {
res.alternative_location = prepare_identifier(al);
}
if let Some(a) = atom {
res.atoms.push(a);
}
Some(res)
} else {
None
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn set_name(&mut self, new_name: &str) -> bool {
if let Some(n) = prepare_identifier(new_name) {
self.name = n;
true
} else {
false
}
}
pub fn alternative_location(&self) -> Option<&str> {
self.alternative_location.as_deref()
}
pub fn set_alternative_location(&mut self, new_loc: &str) -> bool {
if let Some(l) = prepare_identifier(new_loc) {
self.alternative_location = Some(l);
true
} else {
false
}
}
pub fn remove_alternative_location(&mut self) {
self.alternative_location = None;
}
pub fn id(&self) -> (&str, Option<&str>) {
(&self.name, self.alternative_location())
}
pub fn modification(&self) -> Option<&(String, String)> {
self.modification.as_ref()
}
pub fn set_modification(&mut self, new_modification: (String, String)) -> Result<(), String> {
if !valid_identifier(&new_modification.0) {
Err(format!(
"New modification has invalid characters for standard conformer name, conformer: {:?}, standard name \"{}\"",
self.id(), new_modification.0
))
} else if !valid_text(&new_modification.1) {
Err(format!(
"New modification has invalid characters the comment, conformer: {:?}, comment \"{}\"",
self.id(), new_modification.1
))
} else {
self.modification = Some(new_modification);
Ok(())
}
}
pub fn atom_count(&self) -> usize {
self.atoms.len()
}
pub fn atom(&self, index: usize) -> Option<&Atom> {
self.atoms.get(index)
}
pub fn atom_mut(&mut self, index: usize) -> Option<&mut Atom> {
self.atoms.get_mut(index)
}
pub fn atoms(&self) -> impl DoubleEndedIterator<Item = &Atom> + '_ {
self.atoms.iter()
}
pub fn atoms_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut Atom> + '_ {
self.atoms.iter_mut()
}
pub fn add_atom(&mut self, new_atom: Atom) {
self.atoms.push(new_atom);
}
pub fn amino_acid(&self) -> bool {
reference_tables::get_amino_acid_number(self.name()).is_some()
}
pub fn remove_atoms_by<F>(&mut self, predicate: F)
where
F: Fn(&Atom) -> bool,
{
self.atoms.retain(|atom| !predicate(atom));
}
pub fn remove_atom(&mut self, index: usize) {
self.atoms.remove(index);
}
pub fn remove_atom_by_serial_number(&mut self, serial_number: usize) -> bool {
let index = self
.atoms
.iter()
.position(|a| a.serial_number() == serial_number);
if let Some(i) = index {
self.remove_atom(i);
true
} else {
false
}
}
pub fn remove_atom_by_name(&mut self, name: String) -> bool {
let index = self.atoms.iter().position(|a| a.name() == name);
if let Some(i) = index {
self.remove_atom(i);
true
} else {
false
}
}
pub fn apply_transformation(&mut self, transformation: &TransformationMatrix) {
for atom in self.atoms_mut() {
atom.apply_transformation(transformation);
}
}
pub fn join(&mut self, other: Conformer) {
self.atoms.extend(other.atoms);
}
pub fn extend<T: IntoIterator<Item = Atom>>(&mut self, iter: T) {
self.atoms.extend(iter);
}
pub fn sort(&mut self) {
self.atoms.sort();
}
}
impl fmt::Display for Conformer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"CONFORMER ID:{:?}, Atoms:{}",
self.id(),
self.atoms.len(),
)
}
}
impl PartialOrd for Conformer {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.id().cmp(&other.id()))
}
}
impl Ord for Conformer {
fn cmp(&self, other: &Self) -> Ordering {
self.id().cmp(&other.id())
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
#[test]
fn test_text_validation() {
let mut a = Conformer::new("A", None, None).unwrap();
assert_eq!(Conformer::new("R̊", None, None), None);
assert!(!a.set_name("Oͦ"));
assert_eq!(a.name(), "A");
a.set_name("atom");
assert_eq!(a.name(), "ATOM");
assert!(a.set_alternative_location("A"));
assert!(!a.set_alternative_location("Aͦ"));
assert_eq!(a.alternative_location(), Some("A"));
assert!(a
.set_modification(("ALA".to_string(), "Alanine".to_string()))
.is_ok());
assert!(a
.set_modification(("ALAͦ".to_string(), "Alanine".to_string()))
.is_err());
assert!(a
.set_modification(("ALA".to_string(), "Aͦlanine".to_string()))
.is_err());
}
#[test]
fn ordering_and_equality() {
let a = Conformer::new("A", None, None).unwrap();
let b = Conformer::new("A", None, None).unwrap();
let c = Conformer::new("B", None, None).unwrap();
assert_eq!(a, b);
assert_ne!(a, c);
assert!(a < c);
assert!(b < c);
}
#[test]
fn test_empty() {
let a = Conformer::new("A", None, None).unwrap();
assert_eq!(a.modification(), None);
assert_eq!(a.atom_count(), 0);
}
#[test]
fn test_atom() {
let mut a = Conformer::new("A", None, None).unwrap();
let mut atom1 = Atom::new(false, 12, "CB", 1.0, 1.0, 1.0, 1.0, 1.0, "C", 0).unwrap();
let atom2 = Atom::new(false, 13, "CB", 1.0, 1.0, 1.0, 1.0, 1.0, "C", 0).unwrap();
a.add_atom(atom1.clone());
a.add_atom(atom2.clone());
a.add_atom(atom2);
assert_eq!(a.atom(0), Some(&atom1));
assert_eq!(a.atom_mut(0), Some(&mut atom1));
a.remove_atom(0);
assert!(a.remove_atom_by_name("CB".to_string()));
assert!(a.remove_atom_by_serial_number(13));
assert_eq!(a.atom_count(), 0);
}
#[test]
fn check_display() {
let a = Conformer::new("A", None, None).unwrap();
format!("{:?}", a);
format!("{}", a);
}
}