use std::fmt::{Debug, Display, Formatter};
use std::hash::Hash;
use std::ops::{Deref, DerefMut};
use std::str::FromStr;
use ref_cast::{RefCastCustom, ref_cast_custom};
use crate::error::ParseSeqError;
use crate::translation::GeneticCode;
use crate::{DnaIter, DnaSlice, Nucleotide, Symbol};
#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, RefCastCustom)]
#[repr(transparent)]
pub struct Seq<T: ?Sized>(pub T);
impl<T: ?Sized> Seq<T> {
#[ref_cast_custom]
pub fn wrap(slice: &T) -> &Self;
#[ref_cast_custom]
pub fn wrap_mut(slice: &mut T) -> &mut Self;
#[allow(
clippy::needless_pass_by_value,
reason = "consistency with other methods, plus G is likely Copy"
)]
pub fn translated_by<S, G, U>(&self, genetic_code: G) -> Seq<U>
where
for<'a> &'a T: IntoIterator<Item = &'a S>,
S: Nucleotide,
G: GeneticCode,
U: FromIterator<S::Amino>,
{
Seq(self
.0
.into_iter()
.codons()
.map(|codon| genetic_code.translate(codon))
.collect())
}
pub fn translated_to_array_by<S, G, const N: usize>(
&self,
genetic_code: G,
) -> Seq<[<<[S] as DnaSlice>::Nuc as Nucleotide>::Amino; N]>
where
T: AsRef<[S]>,
[S]: DnaSlice,
G: GeneticCode,
{
Seq(self.0.as_ref().translated_to_array_by(genetic_code))
}
pub fn rc_translated_to_array_by<S, G, const N: usize>(
&self,
genetic_code: G,
) -> Seq<[<<[S] as DnaSlice>::Nuc as Nucleotide>::Amino; N]>
where
T: AsRef<[S]>,
[S]: DnaSlice,
G: GeneticCode,
{
Seq(self.0.as_ref().rc_translated_to_array_by(genetic_code))
}
pub fn translated_to_vec_by<S, G>(
&self,
genetic_code: G,
) -> Seq<Vec<<<[S] as DnaSlice>::Nuc as Nucleotide>::Amino>>
where
T: AsRef<[S]>,
[S]: DnaSlice,
G: GeneticCode,
{
Seq(self.0.as_ref().translated_to_vec_by(genetic_code))
}
pub fn rc_translated_to_vec_by<S, G>(
&self,
genetic_code: G,
) -> Seq<Vec<<<[S] as DnaSlice>::Nuc as Nucleotide>::Amino>>
where
T: AsRef<[S]>,
[S]: DnaSlice,
G: GeneticCode,
{
Seq(self.0.as_ref().rc_translated_to_vec_by(genetic_code))
}
}
impl<T: ?Sized> Deref for Seq<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
impl<T: ?Sized> DerefMut for Seq<T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.0
}
}
impl<T: FromIterator<A>, A> FromIterator<A> for Seq<T> {
fn from_iter<U>(iter: U) -> Self
where
U: IntoIterator<Item = A>,
{
Self(T::from_iter(iter))
}
}
impl<T: IntoIterator> IntoIterator for Seq<T> {
type IntoIter = T::IntoIter;
type Item = T::Item;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<T: ?Sized, S> PartialEq<&str> for Seq<T>
where
for<'a> &'a T: IntoIterator<Item = &'a S>,
S: Symbol,
{
fn eq(&self, rhs: &&str) -> bool {
self == *rhs
}
}
impl<T: ?Sized, S> PartialEq<str> for Seq<T>
where
for<'a> &'a T: IntoIterator<Item = &'a S>,
S: Symbol,
{
fn eq(&self, rhs: &str) -> bool {
self.into_iter().copied().map(Ok).eq(iter_symbols(rhs))
}
}
impl<T: ?Sized> Display for Seq<T>
where
for<'a> &'a T: IntoIterator<Item: Display>,
{
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
Display::fmt(&crate::iter::Display::new(&self.0), f)
}
}
impl<T: ?Sized> Debug for Seq<T>
where
for<'a> &'a T: IntoIterator<Item: Display>,
{
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
f.debug_tuple("Seq")
.field(&crate::iter::Display::new(&self.0))
.finish()
}
}
impl<T, U> FromStr for Seq<T>
where
for<'a> &'a T: IntoIterator<Item = &'a U>,
T: FromIterator<U>,
U: Symbol,
{
type Err = ParseSeqError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
iter_symbols(s).collect::<Result<_, _>>().map(Self)
}
}
fn iter_symbols<S: Symbol>(s: &str) -> impl Iterator<Item = Result<S, ParseSeqError>> {
s.split("")
.filter(|c| !c.is_empty())
.enumerate()
.filter(|(_, c)| !c.trim().is_empty()) .map(|(pos, chr)| {
chr.parse().map_err(|_| ParseSeqError {
kind: S::NAME,
expected: S::EXPECTED,
chr: chr.chars().next().expect("BUG: chr was impossibly empty"),
pos,
})
})
}
#[cfg(feature = "serde")]
mod serde_impls {
use std::fmt::{Display, Formatter};
use std::marker::PhantomData;
use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Visitor};
use super::{Seq, Symbol};
impl<T> Serialize for Seq<T>
where
Self: Display,
{
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&self.to_string())
}
}
impl<'de, T, U> Deserialize<'de> for Seq<T>
where
for<'a> &'a T: IntoIterator<Item = &'a U>,
T: FromIterator<U>,
U: Symbol,
{
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_str(SeqVisitor(PhantomData))
}
}
struct SeqVisitor<T>(PhantomData<T>);
impl<T, U> Visitor<'_> for SeqVisitor<T>
where
for<'a> &'a T: IntoIterator<Item = &'a U>,
T: FromIterator<U>,
U: Symbol,
{
type Value = Seq<T>;
fn expecting(&self, f: &mut Formatter) -> std::fmt::Result {
write!(f, "a string of {}s", U::NAME)
}
fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
v.parse().map_err(E::custom)
}
}
}
#[cfg(all(test, feature = "serde"))]
mod serde_tests {
use crate::Dna;
#[test]
fn dna_roundtrip() {
let original_dna: Dna = "CATTAG".parse().unwrap();
let json = serde_json::to_string(&original_dna).unwrap();
assert_eq!(json, "\"CATTAG\"");
let deserialized_dna: Dna = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized_dna, original_dna);
}
#[test]
fn invalid_dna() {
let err = serde_json::from_str::<Dna>("[]").unwrap_err();
assert!(err.to_string().contains("expected a string of nucleotides"));
let err = serde_json::from_str::<Dna>("\"CATXTAG\"").unwrap_err();
assert!(
err.to_string()
.contains("invalid nucleotide 'X' at position 3")
);
}
}
#[cfg(test)]
mod tests {
use std::collections::VecDeque;
use crate::{NCBI1, Nuc, Seq};
#[test]
fn sanity_check_that_seq_works_with_arrays() {
let dna = Nuc::seq(b"ACGT");
assert_eq!(dna, "ACGT");
let peptide = dna.translated_to_vec_by(NCBI1);
assert_eq!(peptide, "T");
}
#[test]
fn sanity_check_that_seq_works_with_vecs() {
let dna = Seq(Nuc::arr(b"ACGT").to_vec());
assert_eq!(dna, "ACGT");
let peptide = dna.translated_to_vec_by(NCBI1);
assert_eq!(peptide, "T");
}
#[test]
fn sanity_check_that_seq_works_with_slices() {
let dna = Seq::wrap(const { &Nuc::arr(b"ACGT") });
assert_eq!(dna, "ACGT");
let peptide = dna.translated_to_vec_by(NCBI1);
assert_eq!(peptide, "T");
}
#[test]
fn sanity_check_that_seq_works_with_vecdeques() {
type VdSeq<T> = Seq<VecDeque<T>>;
let dna = VdSeq::from_iter(Nuc::arr(b"ACGT"));
assert_eq!(dna, "ACGT");
let peptide: VdSeq<_> = dna.translated_by(NCBI1);
assert_eq!(peptide, "T");
}
}