1use std::str::FromStr;
4
5use omics_molecule::compound::Nucleotide;
6
7pub mod snv;
8
9#[derive(Debug)]
11pub enum Error {
12 ParseError(String),
14}
15
16impl std::fmt::Display for Error {
17 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18 match self {
19 Error::ParseError(v) => write!(f, "unable to parse a variant from string: {v}"),
20 }
21 }
22}
23
24impl std::error::Error for Error {}
25
26#[derive(Debug)]
28pub enum Variant<N: Nucleotide> {
29 SingleNucleotideVariation(snv::Variant<N>),
31}
32
33impl<N: Nucleotide> std::str::FromStr for Variant<N>
34where
35 <N as FromStr>::Err: std::fmt::Debug + std::fmt::Display,
36{
37 type Err = Error;
38
39 fn from_str(s: &str) -> Result<Self, Self::Err> {
40 if let Ok(snv) = s.parse::<snv::Variant<N>>() {
41 return Ok(Variant::SingleNucleotideVariation(snv));
42 }
43
44 Err(Error::ParseError(s.to_string()))
45 }
46}
47
48impl<N: Nucleotide> std::fmt::Display for Variant<N>
49where
50 <N as FromStr>::Err: std::fmt::Debug + std::fmt::Display,
51{
52 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53 match self {
54 Variant::SingleNucleotideVariation(variant) => write!(f, "{}", variant),
55 }
56 }
57}
58
59#[cfg(test)]
60mod tests {
61 use omics_coordinate::Strand;
62 use omics_molecule::polymer::dna;
63
64 use super::*;
65
66 #[test]
67 fn it_parses_dna_snvs_correctly() -> Result<(), Box<dyn std::error::Error>> {
68 let variant = "seq0:+:1:A:C".parse::<Variant<dna::Nucleotide>>()?;
69 assert!(matches!(variant, Variant::SingleNucleotideVariation(_)));
70
71 match variant {
72 Variant::SingleNucleotideVariation(snv) => {
73 assert_eq!(snv.coordinate().contig().as_str(), "seq0");
74 assert_eq!(snv.coordinate().strand(), Strand::Positive);
75 assert_eq!(snv.coordinate().position().get(), 1);
76 assert_eq!(snv.reference(), &dna::Nucleotide::A);
77 assert_eq!(snv.alternate(), &dna::Nucleotide::C);
78 }
79 }
80
81 Ok(())
82 }
83
84 #[test]
85 fn it_errors_when_attempting_to_parse_invalid_variants()
86 -> Result<(), Box<dyn std::error::Error>> {
87 let err = "seq0:1:A".parse::<Variant<dna::Nucleotide>>().unwrap_err();
88 assert_eq!(
89 err.to_string(),
90 "unable to parse a variant from string: seq0:1:A"
91 );
92
93 let err = "seq0:1:A:".parse::<Variant<dna::Nucleotide>>().unwrap_err();
94 assert_eq!(
95 err.to_string(),
96 "unable to parse a variant from string: seq0:1:A:"
97 );
98
99 let err = "seq0:A:C:1"
100 .parse::<Variant<dna::Nucleotide>>()
101 .unwrap_err();
102 assert_eq!(
103 err.to_string(),
104 "unable to parse a variant from string: seq0:A:C:1"
105 );
106
107 Ok(())
108 }
109
110 #[test]
111 fn it_serializes_correctly() -> Result<(), Box<dyn std::error::Error>> {
112 let variant = "seq0:+:1:A:C".parse::<Variant<dna::Nucleotide>>()?;
113 assert_eq!(variant.to_string(), "seq0:+:1:A:C");
114
115 let variant = "seq0:+:1:A:C".parse::<Variant<dna::Nucleotide>>()?;
116 assert_eq!(variant.to_string(), "seq0:+:1:A:C");
117
118 Ok(())
119 }
120}