use std::marker::PhantomData;
use serde::{Deserialize, Serialize};
use crate::{
chemistry::{AmbiguousMolecule, MassOutputMode, MolecularFormula, Molecule, SatelliteLabel},
quantities::Multi,
sequence::{
AminoAcid, AtLeast, IsAminoAcid, Linear, SemiAmbiguous, SequencePosition, SimpleLinear,
UnAmbiguous,
},
};
#[derive(Debug, Deserialize, Ord, PartialOrd, Serialize)]
pub struct CheckedAminoAcid<T> {
aminoacid: AminoAcid,
marker: PhantomData<T>,
}
#[expect(non_upper_case_globals, missing_docs)]
impl CheckedAminoAcid<UnAmbiguous> {
pub const A: Self = Self::Alanine;
pub const Ala: Self = Self::Alanine;
pub const Alanine: Self = Self {
aminoacid: AminoAcid::Alanine,
marker: PhantomData,
};
pub const AmbiguousLeucine: Self = Self {
aminoacid: AminoAcid::AmbiguousLeucine,
marker: PhantomData,
};
pub const Arg: Self = Self::Arginine;
pub const Arginine: Self = Self {
aminoacid: AminoAcid::Arginine,
marker: PhantomData,
};
pub const Asn: Self = Self::Asparagine;
pub const Asp: Self = Self::AsparticAcid;
pub const Asparagine: Self = Self {
aminoacid: AminoAcid::Asparagine,
marker: PhantomData,
};
pub const AsparticAcid: Self = Self {
aminoacid: AminoAcid::AsparticAcid,
marker: PhantomData,
};
pub const C: Self = Self::Cysteine;
pub const CANONICAL_AMINO_ACIDS: &'static [Self] = &[
Self::Glycine,
Self::Alanine,
Self::Arginine,
Self::Asparagine,
Self::AsparticAcid,
Self::Cysteine,
Self::Glutamine,
Self::GlutamicAcid,
Self::Histidine,
Self::Leucine,
Self::Isoleucine,
Self::Lysine,
Self::Methionine,
Self::Phenylalanine,
Self::Proline,
Self::Serine,
Self::Threonine,
Self::Tryptophan,
Self::Tyrosine,
Self::Valine,
];
pub const Cys: Self = Self::Cysteine;
pub const Cysteine: Self = Self {
aminoacid: AminoAcid::Cysteine,
marker: PhantomData,
};
pub const D: Self = Self::AsparticAcid;
pub const E: Self = Self::GlutamicAcid;
pub const F: Self = Self::Phenylalanine;
pub const G: Self = Self::Glycine;
pub const Gln: Self = Self::Glutamine;
pub const Glu: Self = Self::GlutamicAcid;
pub const GlutamicAcid: Self = Self {
aminoacid: AminoAcid::GlutamicAcid,
marker: PhantomData,
};
pub const Glutamine: Self = Self {
aminoacid: AminoAcid::Glutamine,
marker: PhantomData,
};
pub const Gly: Self = Self::Glycine;
pub const Glycine: Self = Self {
aminoacid: AminoAcid::Glycine,
marker: PhantomData,
};
pub const H: Self = Self::Histidine;
pub const His: Self = Self::Histidine;
pub const Histidine: Self = Self {
aminoacid: AminoAcid::Histidine,
marker: PhantomData,
};
pub const I: Self = Self::Isoleucine;
pub const Ile: Self = Self::Isoleucine;
pub const Isoleucine: Self = Self {
aminoacid: AminoAcid::Isoleucine,
marker: PhantomData,
};
pub const J: Self = Self::AmbiguousLeucine;
pub const K: Self = Self::Lysine;
pub const L: Self = Self::Leucine;
pub const Leu: Self = Self::Leucine;
pub const Leucine: Self = Self {
aminoacid: AminoAcid::Leucine,
marker: PhantomData,
};
pub const Lys: Self = Self::Lysine;
pub const Lysine: Self = Self {
aminoacid: AminoAcid::Lysine,
marker: PhantomData,
};
pub const M: Self = Self::Methionine;
pub const Met: Self = Self::Methionine;
pub const Methionine: Self = Self {
aminoacid: AminoAcid::Methionine,
marker: PhantomData,
};
pub const N: Self = Self::Asparagine;
pub const O: Self = Self::Pyrrolysine;
pub const P: Self = Self::Proline;
pub const Phe: Self = Self::Phenylalanine;
pub const Phenylalanine: Self = Self {
aminoacid: AminoAcid::Phenylalanine,
marker: PhantomData,
};
pub const Pro: Self = Self::Proline;
pub const Proline: Self = Self {
aminoacid: AminoAcid::Proline,
marker: PhantomData,
};
pub const Pyl: Self = Self::Pyrrolysine;
pub const Pyrrolysine: Self = Self {
aminoacid: AminoAcid::Pyrrolysine,
marker: PhantomData,
};
pub const Q: Self = Self::Glutamine;
pub const R: Self = Self::Arginine;
pub const S: Self = Self::Serine;
pub const Sec: Self = Self::Selenocysteine;
pub const Selenocysteine: Self = Self {
aminoacid: AminoAcid::Selenocysteine,
marker: PhantomData,
};
pub const Ser: Self = Self::Serine;
pub const Serine: Self = Self {
aminoacid: AminoAcid::Serine,
marker: PhantomData,
};
pub const T: Self = Self::Threonine;
pub const Thr: Self = Self::Threonine;
pub const Threonine: Self = Self {
aminoacid: AminoAcid::Threonine,
marker: PhantomData,
};
pub const Trp: Self = Self::Tryptophan;
pub const Tryptophan: Self = Self {
aminoacid: AminoAcid::Tryptophan,
marker: PhantomData,
};
pub const Tyr: Self = Self::Tyrosine;
pub const Tyrosine: Self = Self {
aminoacid: AminoAcid::Tyrosine,
marker: PhantomData,
};
pub const U: Self = Self::Selenocysteine;
pub const UNIQUE_MASS_AMINO_ACIDS: &'static [Self] = &[
Self::Glycine,
Self::Alanine,
Self::Arginine,
Self::Asparagine,
Self::AsparticAcid,
Self::Cysteine,
Self::Glutamine,
Self::GlutamicAcid,
Self::Histidine,
Self::AmbiguousLeucine,
Self::Lysine,
Self::Methionine,
Self::Phenylalanine,
Self::Proline,
Self::Serine,
Self::Threonine,
Self::Tryptophan,
Self::Tyrosine,
Self::Valine,
Self::Selenocysteine,
Self::Pyrrolysine,
];
pub const Unknown: Self = Self {
aminoacid: AminoAcid::Unknown,
marker: PhantomData,
};
pub const V: Self = Self::Valine;
pub const Val: Self = Self::Valine;
pub const Valine: Self = Self {
aminoacid: AminoAcid::Valine,
marker: PhantomData,
};
pub const W: Self = Self::Tryptophan;
pub const X: Self = Self::Unknown;
pub const Xaa: Self = Self::Unknown;
pub const Xle: Self = Self::AmbiguousLeucine;
pub const Y: Self = Self::Tyrosine;
}
#[expect(non_upper_case_globals, missing_docs)]
impl CheckedAminoAcid<SemiAmbiguous> {
pub const AmbiguousAsparagine: Self = Self {
aminoacid: AminoAcid::AmbiguousAsparagine,
marker: PhantomData,
};
pub const AmbiguousGlutamine: Self = Self {
aminoacid: AminoAcid::AmbiguousGlutamine,
marker: PhantomData,
};
pub const Asx: Self = Self::AmbiguousAsparagine;
pub const B: Self = Self::AmbiguousAsparagine;
pub const Glx: Self = Self::AmbiguousGlutamine;
pub const Z: Self = Self::AmbiguousGlutamine;
}
impl From<AminoAcid> for CheckedAminoAcid<SemiAmbiguous> {
fn from(value: AminoAcid) -> Self {
Self::new(value)
}
}
impl From<&AminoAcid> for CheckedAminoAcid<SemiAmbiguous> {
fn from(value: &AminoAcid) -> Self {
Self::new(*value)
}
}
impl From<AminoAcid> for CheckedAminoAcid<SimpleLinear> {
fn from(value: AminoAcid) -> Self {
Self::new(value)
}
}
impl From<&AminoAcid> for CheckedAminoAcid<SimpleLinear> {
fn from(value: &AminoAcid) -> Self {
Self::new(*value)
}
}
impl From<AminoAcid> for CheckedAminoAcid<Linear> {
fn from(value: AminoAcid) -> Self {
Self::new(value)
}
}
impl From<&AminoAcid> for CheckedAminoAcid<Linear> {
fn from(value: &AminoAcid) -> Self {
Self::new(*value)
}
}
impl<Complexity: AtLeast<SemiAmbiguous>> CheckedAminoAcid<Complexity> {
pub const fn new(aminoacid: AminoAcid) -> Self {
Self {
aminoacid,
marker: PhantomData,
}
}
}
impl<T> CheckedAminoAcid<T> {
pub(in crate::sequence) const fn mark<M>(self) -> CheckedAminoAcid<M> {
CheckedAminoAcid {
aminoacid: self.aminoacid,
marker: PhantomData,
}
}
pub fn is_unambiguous(self) -> bool {
self.aminoacid != AminoAcid::AmbiguousAsparagine
&& self.aminoacid != AminoAcid::AmbiguousGlutamine
}
pub fn into_unambiguous(self) -> Option<CheckedAminoAcid<UnAmbiguous>> {
self.is_unambiguous().then_some(self.mark())
}
pub fn canonical_identical(self, rhs: Self) -> bool {
self.aminoacid.canonical_identical(rhs.aminoacid)
}
pub const fn aminoacid(self) -> AminoAcid {
self.aminoacid
}
}
impl<T> IsAminoAcid for CheckedAminoAcid<T> {
fn name(&self) -> std::borrow::Cow<'_, str> {
self.aminoacid.name()
}
fn three_letter_code(&self) -> Option<std::borrow::Cow<'_, str>> {
self.aminoacid.three_letter_code()
}
fn one_letter_code(&self) -> Option<char> {
self.aminoacid.one_letter_code()
}
fn pro_forma_definition(&self) -> std::borrow::Cow<'_, str> {
self.aminoacid.pro_forma_definition()
}
fn satellite_ion_fragments(
&self,
sequence_index: SequencePosition,
peptidoform_index: usize,
peptidoform_ion_index: usize,
) -> Option<std::borrow::Cow<'_, [(SatelliteLabel, MolecularFormula)]>> {
self.aminoacid.satellite_ion_fragments(
sequence_index,
peptidoform_index,
peptidoform_ion_index,
)
}
fn side_chain(
&self,
sequence_index: SequencePosition,
peptidoform_index: usize,
peptidoform_ion_index: usize,
) -> std::borrow::Cow<'_, Multi<MolecularFormula>> {
self.aminoacid
.side_chain(sequence_index, peptidoform_index, peptidoform_ion_index)
}
}
impl Molecule for CheckedAminoAcid<UnAmbiguous> {
fn calculate_mass_inner<Mode: MassOutputMode>(
&self,
sequence_index: SequencePosition,
peptidoform_index: usize,
) -> Mode::Output {
self.aminoacid
.calculate_masses_inner::<Mode>(sequence_index, peptidoform_index, 0)
.single()
.unwrap()
}
}
impl<T> AmbiguousMolecule for CheckedAminoAcid<T> {
fn calculate_masses_inner<Mode: MassOutputMode>(
&self,
sequence_index: SequencePosition,
peptidoform_index: usize,
_peptidoform_ion_index: usize,
) -> Multi<<Mode as MassOutputMode>::Output> {
self.aminoacid
.calculate_masses_inner::<Mode>(sequence_index, peptidoform_index, 0)
}
}
impl<T> Copy for CheckedAminoAcid<T> {}
impl<T> Clone for CheckedAminoAcid<T> {
fn clone(&self) -> Self {
*self
}
}
impl<A, B> PartialEq<CheckedAminoAcid<B>> for CheckedAminoAcid<A> {
fn eq(&self, other: &CheckedAminoAcid<B>) -> bool {
self.aminoacid == other.aminoacid
}
}
impl<T> std::hash::Hash for CheckedAminoAcid<T> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.aminoacid.hash(state);
}
}
impl<T> Eq for CheckedAminoAcid<T> {}
impl<T> Default for CheckedAminoAcid<T> {
fn default() -> Self {
Self {
aminoacid: AminoAcid::default(),
marker: PhantomData,
}
}
}
impl<T> std::fmt::Display for CheckedAminoAcid<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.pro_forma_definition())
}
}
impl std::str::FromStr for CheckedAminoAcid<SemiAmbiguous> {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::try_from(s)
}
}
impl TryFrom<&str> for CheckedAminoAcid<SemiAmbiguous> {
type Error = ();
fn try_from(value: &str) -> Result<Self, Self::Error> {
if value.is_ascii() && value.len() == 1 {
let ch = value.chars().next().unwrap();
ch.try_into()
} else {
Err(())
}
}
}
impl TryFrom<char> for CheckedAminoAcid<SemiAmbiguous> {
type Error = ();
fn try_from(value: char) -> Result<Self, Self::Error> {
if value.is_ascii() {
let num = value as u8;
num.try_into()
} else {
Err(())
}
}
}
impl TryFrom<&u8> for CheckedAminoAcid<SemiAmbiguous> {
type Error = ();
fn try_from(value: &u8) -> Result<Self, Self::Error> {
match value {
b'A' | b'a' => Ok(CheckedAminoAcid::<UnAmbiguous>::Alanine.mark()),
b'B' | b'b' => Ok(Self::AmbiguousAsparagine),
b'C' | b'c' => Ok(CheckedAminoAcid::<UnAmbiguous>::Cysteine.mark()),
b'D' | b'd' => Ok(CheckedAminoAcid::<UnAmbiguous>::AsparticAcid.mark()),
b'E' | b'e' => Ok(CheckedAminoAcid::<UnAmbiguous>::GlutamicAcid.mark()),
b'F' | b'f' => Ok(CheckedAminoAcid::<UnAmbiguous>::Phenylalanine.mark()),
b'G' | b'g' => Ok(CheckedAminoAcid::<UnAmbiguous>::Glycine.mark()),
b'H' | b'h' => Ok(CheckedAminoAcid::<UnAmbiguous>::Histidine.mark()),
b'I' | b'i' => Ok(CheckedAminoAcid::<UnAmbiguous>::Isoleucine.mark()),
b'J' | b'j' => Ok(CheckedAminoAcid::<UnAmbiguous>::AmbiguousLeucine.mark()),
b'K' | b'k' => Ok(CheckedAminoAcid::<UnAmbiguous>::Lysine.mark()),
b'L' | b'l' => Ok(CheckedAminoAcid::<UnAmbiguous>::Leucine.mark()),
b'M' | b'm' => Ok(CheckedAminoAcid::<UnAmbiguous>::Methionine.mark()),
b'N' | b'n' => Ok(CheckedAminoAcid::<UnAmbiguous>::Asparagine.mark()),
b'O' | b'o' => Ok(CheckedAminoAcid::<UnAmbiguous>::Pyrrolysine.mark()),
b'P' | b'p' => Ok(CheckedAminoAcid::<UnAmbiguous>::Proline.mark()),
b'Q' | b'q' => Ok(CheckedAminoAcid::<UnAmbiguous>::Glutamine.mark()),
b'R' | b'r' => Ok(CheckedAminoAcid::<UnAmbiguous>::Arginine.mark()),
b'S' | b's' => Ok(CheckedAminoAcid::<UnAmbiguous>::Serine.mark()),
b'T' | b't' => Ok(CheckedAminoAcid::<UnAmbiguous>::Threonine.mark()),
b'U' | b'u' => Ok(CheckedAminoAcid::<UnAmbiguous>::Selenocysteine.mark()),
b'V' | b'v' => Ok(CheckedAminoAcid::<UnAmbiguous>::Valine.mark()),
b'W' | b'w' => Ok(CheckedAminoAcid::<UnAmbiguous>::Tryptophan.mark()),
b'X' | b'x' => Ok(CheckedAminoAcid::<UnAmbiguous>::Unknown.mark()),
b'Y' | b'y' => Ok(CheckedAminoAcid::<UnAmbiguous>::Tyrosine.mark()),
b'Z' | b'z' => Ok(Self::AmbiguousGlutamine),
_ => Err(()),
}
}
}
impl TryFrom<u8> for CheckedAminoAcid<SemiAmbiguous> {
type Error = ();
fn try_from(value: u8) -> Result<Self, Self::Error> {
Self::try_from(&value)
}
}
impl<C> crate::space::Space for CheckedAminoAcid<C> {
fn space(&self) -> crate::space::UsedSpace {
self.aminoacid.space()
}
}