bio-seq 0.15.2

Bit packed and well-typed biological sequences
Documentation
use bio_seq::prelude::*;
use bio_seq::translation::{
    CodonTable, PartialTranslationTable, STANDARD, TranslationError, TranslationTable,
};

fn message(error: impl std::error::Error) -> String {
    error.to_string()
}

#[test]
fn errors_display_the_offending_codon_or_amino_acid() {
    let table = CodonTable::from_map([(Seq::<Dna>::from(dna!("ATG")), Amino::M)]);

    assert_eq!(
        message(STANDARD.to_codon(Amino::L).unwrap_err()),
        "Multiple codon sequences: L"
    );
    assert_eq!(
        message(STANDARD.try_to_amino(iupac!("NNN")).unwrap_err()),
        "Ambiguous translations for codon: NNN"
    );
    assert_eq!(
        message(STANDARD.try_to_amino(iupac!("A-G")).unwrap_err()),
        "Invalid codon sequence: A-G"
    );
    assert_eq!(
        message(table.try_to_codon(Amino::K).unwrap_err()),
        "Invalid amino acid character: K"
    );
    assert_eq!(
        message(table.try_to_codon(Amino::X).unwrap_err()),
        "Invalid amino acid character: *"
    );
}

#[test]
fn codon_tables_reject_what_they_do_not_contain() {
    let table = CodonTable::from_map([
        (Seq::<Dna>::from(dna!("ATG")), Amino::M),
        (dna!("TAA").into(), Amino::X),
        (dna!("TAG").into(), Amino::X),
    ]);

    assert_eq!(table.try_to_amino(dna!("ATG")), Ok(Amino::M));
    for codon in [dna!("CCC"), dna!("AT"), dna!("ATGA")] {
        assert_eq!(
            table.try_to_amino(codon),
            Err(TranslationError::InvalidCodon(codon.into())),
            "{codon}"
        );
    }

    assert_eq!(table.try_to_codon(Amino::M), Ok(dna!("ATG").into()));
    assert_eq!(
        table.try_to_codon(Amino::X),
        Err(TranslationError::AmbiguousCodon(Amino::X))
    );
    assert_eq!(
        table.try_to_codon(Amino::K),
        Err(TranslationError::InvalidAmino(Amino::K))
    );
}

#[test]
fn only_methionine_and_tryptophan_have_a_single_standard_codon() {
    for amino in Amino::items() {
        let expected = match amino {
            Amino::M => Ok(dna!("ATG").to_owned()),
            Amino::W => Ok(dna!("TGG").to_owned()),
            _ => Err(TranslationError::AmbiguousCodon(amino)),
        };
        assert_eq!(STANDARD.to_codon(amino), expected, "{amino}");
    }
}