use crate::hgvs::edit::{InsertedSequence, NaEdit};
use crate::hgvs::variant::HgvsVariant;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InsertionSizeCount {
pub count: u64,
}
impl InsertionSizeCount {
#[must_use]
pub fn clause(&self) -> &'static str {
"checklist.md:33"
}
#[must_use]
pub fn conformant_spelling(&self) -> String {
format!("insN[{}]", self.count)
}
#[must_use]
pub fn conformant_spelling_for_rna(&self) -> String {
format!("insn[{}]", self.count)
}
}
#[must_use]
pub fn payload_forms_clause(rna_axis: bool) -> &'static str {
if rna_axis {
"RNA/insertion.md:20"
} else {
"DNA/insertion.md:22"
}
}
#[must_use]
pub fn unspecified_run_clause(rna_axis: bool) -> &'static str {
if rna_axis {
"RNA/insertion.md:41"
} else {
"DNA/insertion.md:77"
}
}
#[must_use]
pub fn reference_range_example(rna_axis: bool) -> &'static str {
if rna_axis {
"r.849_850ins858_895"
} else {
"c.849_850ins858_895"
}
}
fn count_in_edit(edit: &NaEdit) -> Option<InsertionSizeCount> {
match edit {
NaEdit::Insertion { sequence } | NaEdit::BreakpointInsertion { sequence } => {
match sequence {
InsertedSequence::Count(n) => Some(InsertionSizeCount { count: *n }),
InsertedSequence::Literal(_)
| InsertedSequence::Range(_, _)
| InsertedSequence::Repeat { .. }
| InsertedSequence::SequenceRepeat { .. }
| InsertedSequence::Complex(_)
| InsertedSequence::Named(_)
| InsertedSequence::Reference(_)
| InsertedSequence::PositionRange { .. }
| InsertedSequence::PositionRangeInv { .. }
| InsertedSequence::UncertainRangeInv { .. }
| InsertedSequence::SpecialPositionRange { .. }
| InsertedSequence::Uncertain
| InsertedSequence::Empty => None,
}
}
NaEdit::Delins { .. }
| NaEdit::DupIns { .. }
| NaEdit::Substitution { .. }
| NaEdit::SubstitutionNoRef { .. }
| NaEdit::Deletion { .. }
| NaEdit::NPaddedDeletion { .. }
| NaEdit::Duplication { .. }
| NaEdit::Inversion { .. }
| NaEdit::Repeat { .. }
| NaEdit::MultiRepeat { .. }
| NaEdit::Identity { .. }
| NaEdit::Conversion { .. }
| NaEdit::Unknown { .. }
| NaEdit::Methylation { .. }
| NaEdit::CopyNumber { .. }
| NaEdit::Splice { .. }
| NaEdit::NoProduct
| NaEdit::PositionOnly => None,
}
}
#[must_use]
pub fn insertion_size_count(variant: &HgvsVariant) -> Option<InsertionSizeCount> {
match variant {
HgvsVariant::Genome(v) => v.loc_edit.edit.inner().and_then(count_in_edit),
HgvsVariant::Cds(v) => v.loc_edit.edit.inner().and_then(count_in_edit),
HgvsVariant::Tx(v) => v.loc_edit.edit.inner().and_then(count_in_edit),
HgvsVariant::Rna(v) => v.loc_edit.edit.inner().and_then(count_in_edit),
HgvsVariant::Mt(v) => v.loc_edit.edit.inner().and_then(count_in_edit),
HgvsVariant::Circular(v) => v.loc_edit.edit.inner().and_then(count_in_edit),
HgvsVariant::Allele(allele) => allele.variants.iter().find_map(insertion_size_count),
HgvsVariant::GenomeRing(ring) => ring
.segments
.iter()
.find_map(|segment| segment.edit.inner().and_then(count_in_edit)),
HgvsVariant::Supernumerary(inner) => insertion_size_count(inner),
HgvsVariant::Protein(_)
| HgvsVariant::RnaFusion(_)
| HgvsVariant::NullAllele
| HgvsVariant::UnknownAllele => None,
}
}
#[must_use]
pub fn states_rna_axis(variant: &HgvsVariant) -> bool {
match variant {
HgvsVariant::Rna(_) => true,
HgvsVariant::Allele(allele) => allele
.variants
.iter()
.find(|member| insertion_size_count(member).is_some())
.is_some_and(states_rna_axis),
HgvsVariant::Supernumerary(inner) => states_rna_axis(inner),
HgvsVariant::Genome(_)
| HgvsVariant::Cds(_)
| HgvsVariant::Tx(_)
| HgvsVariant::Mt(_)
| HgvsVariant::Circular(_)
| HgvsVariant::GenomeRing(_)
| HgvsVariant::Protein(_)
| HgvsVariant::RnaFusion(_)
| HgvsVariant::NullAllele
| HgvsVariant::UnknownAllele => false,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::parse_hgvs;
fn count_of(input: &str) -> Option<InsertionSizeCount> {
insertion_size_count(&parse_hgvs(input).expect("input parses"))
}
#[test]
fn a_bare_count_insert_is_found() {
assert_eq!(
count_of("NC_TEST.1:g.10_11ins6"),
Some(InsertionSizeCount { count: 6 })
);
}
#[test]
fn the_parenthesized_spelling_reaches_the_same_node() {
assert_eq!(
count_of("NC_TEST.1:g.10_11ins(6)"),
Some(InsertionSizeCount { count: 6 })
);
}
#[test]
fn a_position_range_insert_is_not_a_count() {
assert_eq!(count_of("NC_TEST.1:g.10_11ins858_895"), None);
}
#[test]
fn the_conformant_unspecified_run_is_not_a_count() {
assert_eq!(count_of("NC_TEST.1:g.10_11insN[6]"), None);
}
#[test]
fn a_literal_insert_is_not_a_count() {
assert_eq!(count_of("NC_TEST.1:g.10_11insTGCCAT"), None);
}
#[test]
fn a_count_range_is_deliberately_not_matched() {
assert_eq!(count_of("NC_TEST.1:g.10_11ins(6_9)"), None);
}
#[test]
fn a_delins_count_is_out_of_scope() {
assert_eq!(count_of("NC_TEST.1:g.10_11delins6"), None);
}
#[test]
fn a_member_of_a_composite_is_reached() {
assert_eq!(
count_of("NC_TEST.1:g.[10_11ins6;20del]"),
Some(InsertionSizeCount { count: 6 })
);
assert_eq!(
count_of("NC_TEST.1:g.[10_11ins6];[20del]"),
Some(InsertionSizeCount { count: 6 })
);
}
#[test]
fn the_rna_axis_takes_the_lower_case_spelling() {
let found = count_of("NM_TEST.1:r.10_11ins6").expect("stated");
assert_eq!(found.conformant_spelling(), "insN[6]");
assert_eq!(found.conformant_spelling_for_rna(), "insn[6]");
assert!(states_rna_axis(
&parse_hgvs("NM_TEST.1:r.10_11ins6").expect("parses")
));
assert!(!states_rna_axis(
&parse_hgvs("NC_TEST.1:g.10_11ins6").expect("parses")
));
}
}