molframe-cif 0.4.0

PDBx/mmCIF reading and writing for molframe
Documentation
use molframe_core::structure::ChainSequenceExt;
use molframe_core::{InputBuffer, ReadOptions};

const ENTRY: &str = r"data_seq
_entity.id 1
_entity.type polymer
_struct_asym.id A
_struct_asym.entity_id 1
loop_
_entity_poly_seq.entity_id
_entity_poly_seq.num
_entity_poly_seq.mon_id
1 1 ALA
1 2 GLY
1 3 SER
loop_
_atom_site.id
_atom_site.type_symbol
_atom_site.label_atom_id
_atom_site.label_comp_id
_atom_site.label_asym_id
_atom_site.auth_asym_id
_atom_site.label_seq_id
_atom_site.Cartn_x
_atom_site.Cartn_y
_atom_site.Cartn_z
1 C CA ALA A X 1 0 0 0
2 C CA SER A X 3 1 0 0
_struct_ref.id R1
_struct_ref.entity_id 1
_struct_ref.db_name UNP
_struct_ref.db_code TEST_HUMAN
_struct_ref.pdbx_db_accession P00001
_struct_ref.pdbx_seq_one_letter_code 'A G S'
_struct_ref_seq.align_id 1
_struct_ref_seq.ref_id R1
_struct_ref_seq.pdbx_strand_id X
_struct_ref_seq.seq_align_beg 1
_struct_ref_seq.seq_align_end 3
_struct_ref_seq.db_align_beg 10
_struct_ref_seq.db_align_end 12
";

#[test]
fn chain_exposes_observed_canonical_reference_and_missing_positions() {
    let input = InputBuffer::from_bytes(ENTRY.as_bytes().to_vec());
    let structure = match crate::read(&input, &ReadOptions::new()) {
        Ok((structure, _)) => structure,
        Err(findings) => panic!("fixture failed: {findings:?}"),
    };
    let Some(chain) = structure.data().chains().next() else {
        panic!("chain absent")
    };
    assert_eq!(chain.observed_sequence().len(), 2);
    assert_eq!(chain.canonical_sequence().len(), 3);
    assert_eq!(
        chain.reference_sequences()[0].one_letter_code.as_deref(),
        Some("AGS")
    );
    assert_eq!(
        chain
            .sequence_mapping()
            .iter()
            .map(|mapping| mapping.reference_position)
            .collect::<Vec<_>>(),
        [Some(10), Some(12)]
    );
    assert_eq!(chain.missing_residues()[0].canonical_position, 2);
}

#[test]
fn canonical_writing_preserves_reference_sequences_and_alignments() {
    let input = InputBuffer::from_bytes(ENTRY.as_bytes().to_vec());
    let structure = match crate::read(&input, &ReadOptions::new()) {
        Ok((structure, _)) => structure,
        Err(findings) => panic!("fixture failed: {findings:?}"),
    };
    let options = crate::CifWriteOptions::new().with_block_id("references");
    let canonical = crate::write_canonical_with_options(&structure, &options)
        .unwrap_or_else(|error| panic!("write failed: {error}"));
    let input = InputBuffer::from_bytes(canonical.into_bytes());
    let round_tripped = match crate::read(&input, &ReadOptions::new()) {
        Ok((structure, _)) => structure,
        Err(findings) => panic!("canonical output failed: {findings:?}"),
    };
    let Some(chain) = round_tripped.data().chains().next() else {
        panic!("chain absent")
    };
    assert_eq!(
        chain.reference_sequences()[0].accession.as_deref(),
        Some("P00001")
    );
    assert_eq!(chain.sequence_mapping()[0].reference_position, Some(10));
}