Skip to main content

hgvs/parser/
display.rs

1//! Implementation of Display trait.
2//!
3//! Also, we implement a `NoRef` newtype that can be used for suppressing
4//! output of the reference alleles.  This is mainly useful for running the
5//! tests with the same data as the Python hgvs module.
6
7use std::fmt::Display;
8
9use crate::{parser::ds::*, sequences::aa_to_aa3};
10
11/// Newtype that allows to suppress printing of reference bases.
12pub struct NoRef<'a, T>(pub &'a T)
13where
14    T: Display;
15
16impl<T> NoRef<'_, T>
17where
18    T: Display,
19{
20    pub fn inner(&self) -> &T {
21        match self {
22            NoRef(value) => value,
23        }
24    }
25}
26
27impl<T> Display for Mu<T>
28where
29    T: Display,
30{
31    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32        match self {
33            Mu::Certain(value) => write!(f, "{value}"),
34            Mu::Uncertain(value) => write!(f, "({value})"),
35        }
36    }
37}
38
39impl<'a, T> Display for NoRef<'a, Mu<T>>
40where
41    T: Display,
42    NoRef<'a, T>: std::fmt::Display,
43{
44    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45        match self {
46            NoRef(Mu::Certain(value)) => write!(f, "{}", NoRef(value)),
47            NoRef(Mu::Uncertain(value)) => write!(f, "({})", NoRef(value)),
48        }
49    }
50}
51
52impl Display for GeneSymbol {
53    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54        write!(f, "{}", self.value)
55    }
56}
57
58impl Display for NaEdit {
59    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60        match self {
61            NaEdit::RefAlt {
62                reference,
63                alternative,
64            } => match (reference.len(), alternative.len()) {
65                (0, 0) => write!(f, "="),
66                (1, 1) => {
67                    if reference == alternative {
68                        write!(f, "=")
69                    } else {
70                        write!(f, "{reference}>{alternative}")
71                    }
72                }
73                (0, _) => write!(f, "delins{alternative}"),
74                (_, 0) => write!(f, "del{reference}ins"),
75                (_, _) => {
76                    if reference == alternative {
77                        write!(f, "=")
78                    } else {
79                        write!(f, "del{reference}ins{alternative}")
80                    }
81                }
82            },
83            NaEdit::NumAlt { count, alternative } => match (count, alternative.len()) {
84                (0, 0) => write!(f, "="),
85                (0, _) => write!(f, "delins{alternative}"),
86                (_, 0) => write!(f, "del{count}ins"),
87                (_, _) => write!(f, "del{count}ins{alternative}"),
88            },
89            NaEdit::DelRef { reference } => write!(f, "del{reference}"),
90            NaEdit::DelNum { count } => write!(f, "del{count}"),
91            NaEdit::Ins { alternative } => write!(f, "ins{alternative}"),
92            NaEdit::Dup { reference } => write!(f, "dup{reference}"),
93            NaEdit::InvRef { reference } => write!(f, "inv{reference}"),
94            NaEdit::InvNum { count } => write!(f, "inv{count}"),
95        }
96    }
97}
98
99impl Display for NoRef<'_, NaEdit> {
100    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101        match self {
102            NoRef(NaEdit::RefAlt {
103                reference,
104                alternative,
105            }) => match (reference.len(), alternative.len()) {
106                (0, 0) => write!(f, "="),
107                (1, 1) => {
108                    if reference == alternative {
109                        write!(f, "=")
110                    } else {
111                        write!(f, "{reference}>{alternative}")
112                    }
113                }
114                (_, 0) => write!(f, "delins"),
115                (_, _) => {
116                    if reference == alternative {
117                        write!(f, "=")
118                    } else {
119                        write!(f, "delins{alternative}")
120                    }
121                }
122            },
123            NoRef(NaEdit::NumAlt { count, alternative }) => match (count, alternative.len()) {
124                (0, 0) => write!(f, "="),
125                (_, 0) => write!(f, "delins"),
126                (_, _) => write!(f, "delins{alternative}"),
127            },
128            NoRef(NaEdit::DelRef { .. }) | NoRef(NaEdit::DelNum { .. }) => write!(f, "del"),
129            NoRef(NaEdit::Ins { alternative }) => write!(f, "ins{alternative}"),
130            NoRef(NaEdit::Dup { .. }) => write!(f, "dup"),
131            NoRef(NaEdit::InvRef { .. }) | NoRef(NaEdit::InvNum { .. }) => write!(f, "inv"),
132        }
133    }
134}
135
136impl Display for UncertainLengthChange {
137    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
138        match self {
139            UncertainLengthChange::None => write!(f, ""),
140            UncertainLengthChange::Unknown => write!(f, "?"),
141            UncertainLengthChange::Known(count) => write!(f, "{count}"),
142        }
143    }
144}
145
146impl Display for Accession {
147    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
148        write!(f, "{}", self.value)
149    }
150}
151
152impl Display for ProteinEdit {
153    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154        match self {
155            ProteinEdit::Fs {
156                alternative,
157                terminal,
158                length,
159            } => match (alternative, terminal, length) {
160                (None, None, UncertainLengthChange::None) => write!(f, "fsTer"),
161                (None, None, UncertainLengthChange::Unknown) => write!(f, "fsTer?"),
162                (None, None, UncertainLengthChange::Known(count)) => write!(f, "fsTer{count}"),
163                (Some(alt), None, UncertainLengthChange::None) => write!(f, "{alt}fsTer"),
164                (Some(alt), None, UncertainLengthChange::Unknown) => write!(f, "{alt}fsTer?"),
165                (Some(alt), None, UncertainLengthChange::Known(count)) => {
166                    let alt = aa_to_aa3(alt).expect("aa_to_aa3 conversion failed");
167                    write!(f, "{alt}fsTer{count}")
168                }
169                (None, Some(ter), UncertainLengthChange::None) => {
170                    let mut ter = aa_to_aa3(ter).expect("aa_to_aa3 conversion failed");
171                    if ter.is_empty() {
172                        ter = "Ter".to_string();
173                    }
174                    write!(f, "fs{ter}")
175                }
176                (None, Some(ter), UncertainLengthChange::Unknown) => {
177                    let mut ter = aa_to_aa3(ter).expect("aa_to_aa3 conversion failed");
178                    if ter.is_empty() {
179                        ter = "Ter".to_string();
180                    }
181                    write!(f, "fs{ter}?")
182                }
183                (None, Some(ter), UncertainLengthChange::Known(count)) => {
184                    let mut ter = aa_to_aa3(ter).expect("aa_to_aa3 conversion failed");
185                    if ter.is_empty() {
186                        ter = "Ter".to_string();
187                    }
188                    write!(f, "fs{ter}{count}")
189                }
190                (Some(alt), Some(ter), UncertainLengthChange::None) => {
191                    let alt = aa_to_aa3(alt).expect("aa_to_aa3 conversion failed");
192                    let mut ter = aa_to_aa3(ter).expect("aa_to_aa3 conversion failed");
193                    if ter.is_empty() {
194                        ter = "Ter".to_string();
195                    }
196                    write!(f, "{alt}fs{ter}")
197                }
198                (Some(alt), Some(ter), UncertainLengthChange::Unknown) => {
199                    let alt = aa_to_aa3(alt).expect("aa_to_aa3 conversion failed");
200                    let mut ter = aa_to_aa3(ter).expect("aa_to_aa3 conversion failed");
201                    if ter.is_empty() {
202                        ter = "Ter".to_string();
203                    }
204                    write!(f, "{alt}fs{ter}?")
205                }
206                (Some(alt), Some(ter), UncertainLengthChange::Known(count)) => {
207                    let alt = aa_to_aa3(alt).expect("aa_to_aa3 conversion failed");
208                    let mut ter = aa_to_aa3(ter).expect("aa_to_aa3 conversion failed");
209                    if ter.is_empty() {
210                        ter = "Ter".to_string();
211                    }
212                    write!(f, "{alt}fs{ter}{count}")
213                }
214            },
215            ProteinEdit::Ext {
216                aa_ext,
217                ext_aa,
218                change,
219            } => match (aa_ext, ext_aa, change) {
220                (None, None, UncertainLengthChange::None) => write!(f, "ext"),
221                (None, None, UncertainLengthChange::Unknown) => write!(f, "ext?"),
222                (None, None, UncertainLengthChange::Known(count)) => write!(f, "ext{count}"),
223                (Some(alt), None, UncertainLengthChange::None) => {
224                    let alt = aa_to_aa3(alt).expect("aa_to_aa3 conversion failed");
225                    write!(f, "{alt}ext")
226                }
227                (Some(alt), None, UncertainLengthChange::Unknown) => {
228                    let alt = aa_to_aa3(alt).expect("aa_to_aa3 conversion failed");
229                    write!(f, "{alt}ext?")
230                }
231                (Some(alt), None, UncertainLengthChange::Known(count)) => {
232                    let alt = aa_to_aa3(alt).expect("aa_to_aa3 conversion failed");
233                    write!(f, "{alt}ext{count}")
234                }
235                (None, Some(ter), UncertainLengthChange::None) => write!(f, "ext{ter}"),
236                (None, Some(ter), UncertainLengthChange::Unknown) => write!(f, "ext{ter}?"),
237                (None, Some(ter), UncertainLengthChange::Known(count)) => {
238                    let ter = aa_to_aa3(ter).expect("aa_to_aa3 conversion failed");
239                    write!(f, "ext{ter}{count}")
240                }
241                (Some(alt), Some(ter), UncertainLengthChange::None) => {
242                    let alt = aa_to_aa3(alt).expect("aa_to_aa3 conversion failed");
243                    let ter = aa_to_aa3(ter).expect("aa_to_aa3 conversion failed");
244                    write!(f, "{alt}ext{ter}")
245                }
246                (Some(alt), Some(ter), UncertainLengthChange::Unknown) => {
247                    let alt = aa_to_aa3(alt).expect("aa_to_aa3 conversion failed");
248                    let ter = aa_to_aa3(ter).expect("aa_to_aa3 conversion failed");
249                    write!(f, "{alt}ext{ter}?")
250                }
251                (Some(alt), Some(ter), UncertainLengthChange::Known(count)) => {
252                    let alt = aa_to_aa3(alt).expect("aa_to_aa3 conversion failed");
253                    let ter = aa_to_aa3(ter).expect("aa_to_aa3 conversion failed");
254                    write!(f, "{alt}ext{ter}{count}")
255                }
256            },
257            ProteinEdit::Subst { alternative } => {
258                let alternative = aa_to_aa3(alternative).expect("aa_to_aa3 conversion failed");
259                if alternative.is_empty() {
260                    write!(f, "=")
261                } else {
262                    write!(f, "{alternative}")
263                }
264            }
265            ProteinEdit::DelIns { alternative } => {
266                let alternative = aa_to_aa3(alternative).expect("aa_to_aa3 conversion failed");
267                write!(f, "delins{alternative}")
268            }
269            ProteinEdit::Ins { alternative } => {
270                let alternative = aa_to_aa3(alternative).expect("aa_to_aa3 conversion failed");
271                write!(f, "ins{alternative}")
272            }
273            ProteinEdit::Del => write!(f, "del"),
274            ProteinEdit::Dup => write!(f, "dup"),
275            ProteinEdit::Ident => write!(f, "="),
276        }
277    }
278}
279
280impl Display for ProtPos {
281    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
282        let aa = aa_to_aa3(&self.aa).expect("aa_to_aa3 conversion failed");
283        write!(f, "{aa}{}", self.number)
284    }
285}
286
287impl Display for ProtInterval {
288    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
289        write!(f, "{}", self.start)?;
290        if self.start != self.end {
291            write!(f, "_{}", self.end)?;
292        }
293        Ok(())
294    }
295}
296
297impl Display for ProtLocEdit {
298    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
299        // TODO: make configurable whether inferred protein is uncertain or not?
300        match self {
301            ProtLocEdit::Ordinary { loc, edit } => write!(f, "{loc}{edit}"),
302            ProtLocEdit::NoChange => write!(f, "="),
303            ProtLocEdit::NoChangeUncertain => write!(f, "(=)"),
304            ProtLocEdit::NoProtein => write!(f, "0"),
305            ProtLocEdit::NoProteinUncertain => write!(f, "0?"),
306            ProtLocEdit::Unknown => write!(f, "?"),
307            ProtLocEdit::InitiationUncertain => write!(f, "Met1?"),
308        }
309    }
310}
311
312impl Display for NoRef<'_, ProtLocEdit> {
313    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
314        self.inner().fmt(f)
315    }
316}
317
318impl Display for CdsLocEdit {
319    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
320        write!(f, "{}{}", self.loc, self.edit)
321    }
322}
323
324impl Display for NoRef<'_, CdsLocEdit> {
325    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
326        write!(f, "{}{}", self.inner().loc, NoRef(&self.inner().edit))
327    }
328}
329
330impl Display for CdsInterval {
331    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
332        write!(f, "{}", self.start)?;
333        if self.start != self.end {
334            write!(f, "_{}", self.end)?;
335        }
336        Ok(())
337    }
338}
339
340impl Display for CdsPos {
341    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
342        if self.cds_from == CdsFrom::End {
343            write!(f, "*")?;
344        }
345
346        write!(f, "{}", self.base)?;
347
348        if let Some(offset) = self.offset {
349            if offset > 0 {
350                write!(f, "+")?;
351            }
352            write!(f, "{offset}")?;
353        }
354
355        Ok(())
356    }
357}
358
359impl Display for TxLocEdit {
360    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
361        write!(f, "{}{}", self.loc, self.edit)
362    }
363}
364
365impl Display for NoRef<'_, TxLocEdit> {
366    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
367        write!(f, "{}{}", self.inner().loc, NoRef(&self.inner().edit))
368    }
369}
370
371impl Display for TxInterval {
372    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
373        write!(f, "{}", self.start)?;
374        if self.start != self.end {
375            write!(f, "_{}", self.end)?;
376        }
377        Ok(())
378    }
379}
380
381impl Display for TxPos {
382    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
383        write!(f, "{}", self.base)?;
384
385        if let Some(offset) = self.offset {
386            if offset > 0 {
387                write!(f, "+")?;
388            }
389            write!(f, "{offset}")?;
390        }
391
392        Ok(())
393    }
394}
395
396impl Display for RnaLocEdit {
397    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
398        write!(f, "{}{}", self.loc, self.edit)
399    }
400}
401
402impl Display for NoRef<'_, RnaLocEdit> {
403    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
404        write!(f, "{}{}", self.inner().loc, NoRef(&self.inner().edit))
405    }
406}
407
408impl Display for RnaInterval {
409    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
410        write!(f, "{}", self.start)?;
411        if self.start != self.end {
412            write!(f, "_{}", self.end)?;
413        }
414        Ok(())
415    }
416}
417
418impl Display for RnaPos {
419    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
420        write!(f, "{}", self.base)?;
421
422        if let Some(offset) = self.offset {
423            if offset > 0 {
424                write!(f, "+")?;
425            }
426            write!(f, "{offset}")?;
427        }
428
429        Ok(())
430    }
431}
432
433impl Display for GenomeLocEdit {
434    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
435        write!(f, "{}{}", self.loc, self.edit)
436    }
437}
438
439impl Display for NoRef<'_, GenomeLocEdit> {
440    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
441        write!(f, "{}{}", self.inner().loc, NoRef(&self.inner().edit))
442    }
443}
444
445impl Display for GenomeInterval {
446    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
447        match self.start {
448            Some(begin) => write!(f, "{begin}")?,
449            None => write!(f, "?")?,
450        }
451        if self.start != self.end {
452            match self.end {
453                Some(end) => write!(f, "_{end}")?,
454                None => write!(f, "_?")?,
455            }
456        }
457        Ok(())
458    }
459}
460
461impl Display for MtLocEdit {
462    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
463        write!(f, "{}{}", self.loc, self.edit)
464    }
465}
466
467impl Display for NoRef<'_, MtLocEdit> {
468    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
469        write!(f, "{}{}", self.inner().loc, NoRef(&self.inner().edit))
470    }
471}
472
473impl Display for MtInterval {
474    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
475        match self.start {
476            Some(begin) => write!(f, "{begin}")?,
477            None => write!(f, "?")?,
478        }
479        if self.start != self.end {
480            match self.end {
481                Some(end) => write!(f, "_{end}")?,
482                None => write!(f, "_?")?,
483            }
484        }
485        Ok(())
486    }
487}
488
489impl Display for HgvsVariant {
490    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
491        match self {
492            HgvsVariant::CdsVariant {
493                accession,
494                gene_symbol,
495                loc_edit,
496            } => {
497                write!(f, "{accession}")?;
498                if let Some(gene_symbol) = gene_symbol {
499                    write!(f, "({gene_symbol})")?;
500                }
501                write!(f, ":c.{loc_edit}")
502            }
503            HgvsVariant::GenomeVariant {
504                accession,
505                gene_symbol,
506                loc_edit,
507            } => {
508                write!(f, "{accession}")?;
509                if let Some(gene_symbol) = gene_symbol {
510                    write!(f, "({gene_symbol})")?;
511                }
512                write!(f, ":g.{loc_edit}")
513            }
514            HgvsVariant::MtVariant {
515                accession,
516                gene_symbol,
517                loc_edit,
518            } => {
519                write!(f, "{accession}")?;
520                if let Some(gene_symbol) = gene_symbol {
521                    write!(f, "({gene_symbol})")?;
522                }
523                write!(f, ":m.{loc_edit}")
524            }
525            HgvsVariant::TxVariant {
526                accession,
527                gene_symbol,
528                loc_edit,
529            } => {
530                write!(f, "{accession}")?;
531                if let Some(gene_symbol) = gene_symbol {
532                    write!(f, "({gene_symbol})")?;
533                }
534                write!(f, ":n.{loc_edit}")
535            }
536            HgvsVariant::ProtVariant {
537                accession,
538                gene_symbol,
539                loc_edit,
540            } => {
541                write!(f, "{accession}")?;
542                if let Some(gene_symbol) = gene_symbol {
543                    write!(f, "({gene_symbol})")?;
544                }
545                write!(f, ":p.{loc_edit}")
546            }
547            HgvsVariant::RnaVariant {
548                accession,
549                gene_symbol,
550                loc_edit,
551            } => {
552                write!(f, "{accession}")?;
553                if let Some(gene_symbol) = gene_symbol {
554                    write!(f, "({gene_symbol})")?;
555                }
556                write!(f, ":r.{loc_edit}")
557            }
558        }
559    }
560}
561
562impl Display for NoRef<'_, HgvsVariant> {
563    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
564        match self {
565            NoRef(HgvsVariant::CdsVariant {
566                accession,
567                gene_symbol,
568                loc_edit,
569            }) => {
570                write!(f, "{accession}")?;
571                if let Some(gene_symbol) = gene_symbol {
572                    write!(f, "({gene_symbol})")?;
573                }
574                write!(f, ":c.{}", NoRef(loc_edit))
575            }
576            NoRef(HgvsVariant::GenomeVariant {
577                accession,
578                gene_symbol,
579                loc_edit,
580            }) => {
581                write!(f, "{accession}")?;
582                if let Some(gene_symbol) = gene_symbol {
583                    write!(f, "({gene_symbol})")?;
584                }
585                write!(f, ":g.{}", NoRef(loc_edit))
586            }
587            NoRef(HgvsVariant::MtVariant {
588                accession,
589                gene_symbol,
590                loc_edit,
591            }) => {
592                write!(f, "{accession}")?;
593                if let Some(gene_symbol) = gene_symbol {
594                    write!(f, "({gene_symbol})")?;
595                }
596                write!(f, ":m.{}", NoRef(loc_edit))
597            }
598            NoRef(HgvsVariant::TxVariant {
599                accession,
600                gene_symbol,
601                loc_edit,
602            }) => {
603                write!(f, "{accession}")?;
604                if let Some(gene_symbol) = gene_symbol {
605                    write!(f, "({gene_symbol})")?;
606                }
607                write!(f, ":n.{}", NoRef(loc_edit))
608            }
609            NoRef(HgvsVariant::ProtVariant {
610                accession,
611                gene_symbol,
612                loc_edit,
613            }) => {
614                write!(f, "{accession}")?;
615                if let Some(gene_symbol) = gene_symbol {
616                    write!(f, "({gene_symbol})")?;
617                }
618                write!(f, ":p.{}", NoRef(loc_edit))
619            }
620            NoRef(HgvsVariant::RnaVariant {
621                accession,
622                gene_symbol,
623                loc_edit,
624            }) => {
625                write!(f, "{accession}")?;
626                if let Some(gene_symbol) = gene_symbol {
627                    write!(f, "({gene_symbol})")?;
628                }
629                write!(f, ":r.{}", NoRef(loc_edit))
630            }
631        }
632    }
633}
634
635#[cfg(test)]
636mod test {
637    use anyhow::Error;
638    use std::{
639        fs::File,
640        io::{BufRead, BufReader},
641        str::FromStr,
642    };
643
644    use pretty_assertions::assert_eq;
645
646    use crate::parser::{
647        Accession, CdsFrom, CdsInterval, CdsLocEdit, CdsPos, GeneSymbol, GenomeInterval,
648        GenomeLocEdit, HgvsVariant, MtInterval, MtLocEdit, Mu, NaEdit, ProtInterval, ProtLocEdit,
649        ProtPos, ProteinEdit, RnaInterval, RnaLocEdit, RnaPos, TxInterval, TxLocEdit, TxPos,
650        UncertainLengthChange,
651    };
652
653    #[test]
654    fn mu() {
655        assert_eq!(format!("{}", Mu::Certain(42)), "42".to_string());
656        assert_eq!(format!("{}", Mu::Uncertain(42)), "(42)".to_string());
657    }
658
659    #[test]
660    fn gene_symbol() {
661        assert_eq!(
662            format!(
663                "{}",
664                GeneSymbol {
665                    value: "TTN".to_string()
666                }
667            ),
668            "TTN".to_string()
669        );
670    }
671
672    #[test]
673    fn na_edit_ref_alt() {
674        assert_eq!(
675            format!(
676                "{}",
677                NaEdit::RefAlt {
678                    reference: "".to_string(),
679                    alternative: "".to_string()
680                }
681            ),
682            "=".to_string()
683        );
684
685        assert_eq!(
686            format!(
687                "{}",
688                NaEdit::RefAlt {
689                    reference: "C".to_string(),
690                    alternative: "T".to_string()
691                }
692            ),
693            "C>T".to_string()
694        );
695
696        assert_eq!(
697            format!(
698                "{}",
699                NaEdit::RefAlt {
700                    reference: "CC".to_string(),
701                    alternative: "T".to_string()
702                }
703            ),
704            "delCCinsT".to_string()
705        );
706
707        assert_eq!(
708            format!(
709                "{}",
710                NaEdit::RefAlt {
711                    reference: "C".to_string(),
712                    alternative: "".to_string()
713                }
714            ),
715            "delCins".to_string()
716        );
717
718        assert_eq!(
719            format!(
720                "{}",
721                NaEdit::RefAlt {
722                    reference: "".to_string(),
723                    alternative: "C".to_string()
724                }
725            ),
726            "delinsC".to_string()
727        );
728    }
729
730    #[test]
731    fn na_edit_num_alt() {
732        assert_eq!(
733            format!(
734                "{}",
735                NaEdit::NumAlt {
736                    count: 0,
737                    alternative: "".to_string()
738                }
739            ),
740            "=".to_string()
741        );
742
743        assert_eq!(
744            format!(
745                "{}",
746                NaEdit::NumAlt {
747                    count: 0,
748                    alternative: "T".to_string()
749                }
750            ),
751            "delinsT".to_string()
752        );
753        assert_eq!(
754            format!(
755                "{}",
756                NaEdit::NumAlt {
757                    count: 3,
758                    alternative: "".to_string()
759                }
760            ),
761            "del3ins".to_string()
762        );
763
764        assert_eq!(
765            format!(
766                "{}",
767                NaEdit::NumAlt {
768                    count: 3,
769                    alternative: "T".to_string()
770                }
771            ),
772            "del3insT".to_string()
773        );
774    }
775
776    #[test]
777    fn na_edit_del_ref() {
778        assert_eq!(
779            format!(
780                "{}",
781                NaEdit::DelRef {
782                    reference: "T".to_string()
783                }
784            ),
785            "delT".to_string()
786        );
787    }
788
789    #[test]
790    fn na_edit_del_num() {
791        assert_eq!(
792            format!("{}", NaEdit::DelNum { count: 3 }),
793            "del3".to_string()
794        );
795    }
796
797    #[test]
798    fn na_edit_ins() {
799        assert_eq!(
800            format!(
801                "{}",
802                NaEdit::Ins {
803                    alternative: "T".to_string()
804                }
805            ),
806            "insT".to_string()
807        );
808    }
809
810    #[test]
811    fn na_edit_dup() {
812        assert_eq!(
813            format!(
814                "{}",
815                NaEdit::Dup {
816                    reference: "T".to_string()
817                }
818            ),
819            "dupT".to_string()
820        );
821    }
822
823    #[test]
824    fn na_edit_inv_ref() {
825        assert_eq!(
826            format!(
827                "{}",
828                NaEdit::InvRef {
829                    reference: "T".to_string()
830                }
831            ),
832            "invT".to_string()
833        );
834    }
835
836    #[test]
837    fn na_edit_inv_num() {
838        assert_eq!(
839            format!("{}", NaEdit::InvNum { count: 3 }),
840            "inv3".to_string()
841        );
842    }
843
844    #[test]
845    fn uncertain_length_change() {
846        assert_eq!(format!("{}", UncertainLengthChange::None), "".to_string(),);
847        assert_eq!(
848            format!("{}", UncertainLengthChange::Unknown),
849            "?".to_string(),
850        );
851        assert_eq!(
852            format!("{}", UncertainLengthChange::Known(42)),
853            "42".to_string(),
854        );
855    }
856
857    #[test]
858    fn accession() {
859        assert_eq!(
860            format!(
861                "{}",
862                Accession {
863                    value: "TTN".to_string()
864                }
865            ),
866            "TTN".to_string()
867        )
868    }
869
870    #[test]
871    fn protein_edit_fs() {
872        assert_eq!(
873            format!(
874                "{}",
875                ProteinEdit::Fs {
876                    alternative: None,
877                    terminal: None,
878                    length: UncertainLengthChange::None,
879                }
880            ),
881            "fsTer".to_string(),
882        );
883        assert_eq!(
884            format!(
885                "{}",
886                ProteinEdit::Fs {
887                    alternative: None,
888                    terminal: None,
889                    length: UncertainLengthChange::Unknown,
890                }
891            ),
892            "fsTer?".to_string(),
893        );
894        assert_eq!(
895            format!(
896                "{}",
897                ProteinEdit::Fs {
898                    alternative: None,
899                    terminal: None,
900                    length: UncertainLengthChange::Known(42),
901                }
902            ),
903            "fsTer42".to_string(),
904        );
905
906        assert_eq!(
907            format!(
908                "{}",
909                ProteinEdit::Fs {
910                    alternative: Some("Leu".to_string()),
911                    terminal: None,
912                    length: UncertainLengthChange::None,
913                }
914            ),
915            "LeufsTer".to_string(),
916        );
917        assert_eq!(
918            format!(
919                "{}",
920                ProteinEdit::Fs {
921                    alternative: Some("Leu".to_string()),
922                    terminal: None,
923                    length: UncertainLengthChange::Unknown,
924                }
925            ),
926            "LeufsTer?".to_string(),
927        );
928        assert_eq!(
929            format!(
930                "{}",
931                ProteinEdit::Fs {
932                    alternative: Some("Leu".to_string()),
933                    terminal: None,
934                    length: UncertainLengthChange::Known(42),
935                }
936            ),
937            "LeufsTer42".to_string(),
938        );
939
940        assert_eq!(
941            format!(
942                "{}",
943                ProteinEdit::Fs {
944                    alternative: None,
945                    terminal: Some("Ter".to_string()),
946                    length: UncertainLengthChange::None,
947                }
948            ),
949            "fsTer".to_string(),
950        );
951        assert_eq!(
952            format!(
953                "{}",
954                ProteinEdit::Fs {
955                    alternative: None,
956                    terminal: Some("Ter".to_string()),
957                    length: UncertainLengthChange::Unknown,
958                }
959            ),
960            "fsTer?".to_string(),
961        );
962        assert_eq!(
963            format!(
964                "{}",
965                ProteinEdit::Fs {
966                    alternative: None,
967                    terminal: Some("Ter".to_string()),
968                    length: UncertainLengthChange::Known(42),
969                }
970            ),
971            "fsTer42".to_string(),
972        );
973
974        assert_eq!(
975            format!(
976                "{}",
977                ProteinEdit::Fs {
978                    alternative: Some("Leu".to_string()),
979                    terminal: Some("Ter".to_string()),
980                    length: UncertainLengthChange::None,
981                }
982            ),
983            "LeufsTer".to_string(),
984        );
985        assert_eq!(
986            format!(
987                "{}",
988                ProteinEdit::Fs {
989                    alternative: Some("Leu".to_string()),
990                    terminal: Some("Ter".to_string()),
991                    length: UncertainLengthChange::Unknown,
992                }
993            ),
994            "LeufsTer?".to_string(),
995        );
996        assert_eq!(
997            format!(
998                "{}",
999                ProteinEdit::Fs {
1000                    alternative: Some("Leu".to_string()),
1001                    terminal: Some("Ter".to_string()),
1002                    length: UncertainLengthChange::Known(42),
1003                }
1004            ),
1005            "LeufsTer42".to_string(),
1006        );
1007    }
1008
1009    #[test]
1010    fn protein_edit_ext() {
1011        assert_eq!(
1012            format!(
1013                "{}",
1014                ProteinEdit::Ext {
1015                    aa_ext: None,
1016                    ext_aa: None,
1017                    change: UncertainLengthChange::None,
1018                }
1019            ),
1020            "ext".to_string(),
1021        );
1022        assert_eq!(
1023            format!(
1024                "{}",
1025                ProteinEdit::Ext {
1026                    aa_ext: None,
1027                    ext_aa: None,
1028                    change: UncertainLengthChange::Unknown,
1029                }
1030            ),
1031            "ext?".to_string(),
1032        );
1033        assert_eq!(
1034            format!(
1035                "{}",
1036                ProteinEdit::Ext {
1037                    aa_ext: None,
1038                    ext_aa: None,
1039                    change: UncertainLengthChange::Known(42),
1040                }
1041            ),
1042            "ext42".to_string(),
1043        );
1044
1045        assert_eq!(
1046            format!(
1047                "{}",
1048                ProteinEdit::Ext {
1049                    aa_ext: Some("Leu".to_string()),
1050                    ext_aa: None,
1051                    change: UncertainLengthChange::None,
1052                }
1053            ),
1054            "Leuext".to_string(),
1055        );
1056        assert_eq!(
1057            format!(
1058                "{}",
1059                ProteinEdit::Ext {
1060                    aa_ext: Some("Leu".to_string()),
1061                    ext_aa: None,
1062                    change: UncertainLengthChange::Unknown,
1063                }
1064            ),
1065            "Leuext?".to_string(),
1066        );
1067        assert_eq!(
1068            format!(
1069                "{}",
1070                ProteinEdit::Ext {
1071                    aa_ext: Some("Leu".to_string()),
1072                    ext_aa: None,
1073                    change: UncertainLengthChange::Known(42),
1074                }
1075            ),
1076            "Leuext42".to_string(),
1077        );
1078
1079        assert_eq!(
1080            format!(
1081                "{}",
1082                ProteinEdit::Ext {
1083                    aa_ext: None,
1084                    ext_aa: Some("Thr".to_string()),
1085                    change: UncertainLengthChange::None,
1086                }
1087            ),
1088            "extThr".to_string(),
1089        );
1090        assert_eq!(
1091            format!(
1092                "{}",
1093                ProteinEdit::Ext {
1094                    aa_ext: None,
1095                    ext_aa: Some("Thr".to_string()),
1096                    change: UncertainLengthChange::Unknown,
1097                }
1098            ),
1099            "extThr?".to_string(),
1100        );
1101        assert_eq!(
1102            format!(
1103                "{}",
1104                ProteinEdit::Ext {
1105                    aa_ext: None,
1106                    ext_aa: Some("Thr".to_string()),
1107                    change: UncertainLengthChange::Known(42),
1108                }
1109            ),
1110            "extThr42".to_string(),
1111        );
1112
1113        assert_eq!(
1114            format!(
1115                "{}",
1116                ProteinEdit::Ext {
1117                    aa_ext: Some("Leu".to_string()),
1118                    ext_aa: Some("Thr".to_string()),
1119                    change: UncertainLengthChange::None,
1120                }
1121            ),
1122            "LeuextThr".to_string(),
1123        );
1124        assert_eq!(
1125            format!(
1126                "{}",
1127                ProteinEdit::Ext {
1128                    aa_ext: Some("Leu".to_string()),
1129                    ext_aa: Some("Thr".to_string()),
1130                    change: UncertainLengthChange::Unknown,
1131                }
1132            ),
1133            "LeuextThr?".to_string(),
1134        );
1135        assert_eq!(
1136            format!(
1137                "{}",
1138                ProteinEdit::Ext {
1139                    aa_ext: Some("Leu".to_string()),
1140                    ext_aa: Some("Thr".to_string()),
1141                    change: UncertainLengthChange::Known(42),
1142                }
1143            ),
1144            "LeuextThr42".to_string(),
1145        );
1146    }
1147
1148    #[test]
1149    fn protein_edit_subst() {
1150        assert_eq!(
1151            format!(
1152                "{}",
1153                ProteinEdit::Subst {
1154                    alternative: "Leu".to_string()
1155                }
1156            ),
1157            "Leu".to_string(),
1158        );
1159    }
1160
1161    #[test]
1162    fn protein_edit_del_ins() {
1163        assert_eq!(
1164            format!(
1165                "{}",
1166                ProteinEdit::DelIns {
1167                    alternative: "Leu".to_string()
1168                }
1169            ),
1170            "delinsLeu".to_string(),
1171        );
1172    }
1173
1174    #[test]
1175    fn protein_edit_ins() {
1176        assert_eq!(
1177            format!(
1178                "{}",
1179                ProteinEdit::Ins {
1180                    alternative: "Leu".to_string()
1181                }
1182            ),
1183            "insLeu".to_string(),
1184        );
1185    }
1186
1187    #[test]
1188    fn protein_edit_del() {
1189        assert_eq!(format!("{}", ProteinEdit::Del), "del".to_string(),);
1190    }
1191
1192    #[test]
1193    fn protein_edit_dup() {
1194        assert_eq!(format!("{}", ProteinEdit::Dup), "dup".to_string(),);
1195    }
1196
1197    #[test]
1198    fn protein_edit_ident() {
1199        assert_eq!(format!("{}", ProteinEdit::Ident), "=".to_string(),);
1200    }
1201
1202    #[test]
1203    fn cds_pos() {
1204        assert_eq!(
1205            format!(
1206                "{}",
1207                CdsPos {
1208                    base: 42,
1209                    offset: None,
1210                    cds_from: CdsFrom::Start,
1211                }
1212            ),
1213            "42".to_string(),
1214        );
1215
1216        assert_eq!(
1217            format!(
1218                "{}",
1219                CdsPos {
1220                    base: 42,
1221                    offset: None,
1222                    cds_from: CdsFrom::End,
1223                }
1224            ),
1225            "*42".to_string(),
1226        );
1227
1228        assert_eq!(
1229            format!(
1230                "{}",
1231                CdsPos {
1232                    base: 42,
1233                    offset: Some(10),
1234                    cds_from: CdsFrom::Start,
1235                }
1236            ),
1237            "42+10".to_string(),
1238        );
1239
1240        assert_eq!(
1241            format!(
1242                "{}",
1243                CdsPos {
1244                    base: 42,
1245                    offset: Some(10),
1246                    cds_from: CdsFrom::End,
1247                }
1248            ),
1249            "*42+10".to_string(),
1250        );
1251
1252        assert_eq!(
1253            format!(
1254                "{}",
1255                CdsPos {
1256                    base: 42,
1257                    offset: Some(-10),
1258                    cds_from: CdsFrom::Start,
1259                }
1260            ),
1261            "42-10".to_string(),
1262        );
1263
1264        assert_eq!(
1265            format!(
1266                "{}",
1267                CdsPos {
1268                    base: 42,
1269                    offset: Some(-10),
1270                    cds_from: CdsFrom::End,
1271                }
1272            ),
1273            "*42-10".to_string(),
1274        );
1275    }
1276
1277    #[test]
1278    fn cds_interval() {
1279        assert_eq!(
1280            format!(
1281                "{}",
1282                CdsInterval {
1283                    start: CdsPos {
1284                        base: 42,
1285                        offset: Some(-10),
1286                        cds_from: CdsFrom::Start,
1287                    },
1288                    end: CdsPos {
1289                        base: 42,
1290                        offset: Some(10),
1291                        cds_from: CdsFrom::Start,
1292                    }
1293                }
1294            ),
1295            "42-10_42+10".to_string(),
1296        );
1297
1298        assert_eq!(
1299            format!(
1300                "{}",
1301                CdsInterval {
1302                    start: CdsPos {
1303                        base: 42,
1304                        offset: Some(10),
1305                        cds_from: CdsFrom::Start,
1306                    },
1307                    end: CdsPos {
1308                        base: 42,
1309                        offset: Some(10),
1310                        cds_from: CdsFrom::Start,
1311                    }
1312                }
1313            ),
1314            "42+10".to_string(),
1315        );
1316    }
1317
1318    #[test]
1319    fn cds_loc_edit() {
1320        assert_eq!(
1321            format!(
1322                "{}",
1323                CdsLocEdit {
1324                    loc: Mu::Certain(CdsInterval {
1325                        start: CdsPos {
1326                            base: 42,
1327                            offset: Some(-10),
1328                            cds_from: CdsFrom::Start,
1329                        },
1330                        end: CdsPos {
1331                            base: 42,
1332                            offset: Some(10),
1333                            cds_from: CdsFrom::Start,
1334                        }
1335                    }),
1336                    edit: Mu::Certain(NaEdit::RefAlt {
1337                        reference: "".to_string(),
1338                        alternative: "".to_string()
1339                    })
1340                }
1341            ),
1342            "42-10_42+10=".to_string(),
1343        );
1344    }
1345
1346    #[test]
1347    fn tx_pos() {
1348        assert_eq!(
1349            format!(
1350                "{}",
1351                TxPos {
1352                    base: 42,
1353                    offset: None,
1354                }
1355            ),
1356            "42".to_string(),
1357        );
1358
1359        assert_eq!(
1360            format!(
1361                "{}",
1362                TxPos {
1363                    base: 42,
1364                    offset: Some(10),
1365                }
1366            ),
1367            "42+10".to_string(),
1368        );
1369
1370        assert_eq!(
1371            format!(
1372                "{}",
1373                TxPos {
1374                    base: 42,
1375                    offset: Some(-10),
1376                }
1377            ),
1378            "42-10".to_string(),
1379        );
1380    }
1381
1382    #[test]
1383    fn tx_interval() {
1384        assert_eq!(
1385            format!(
1386                "{}",
1387                TxInterval {
1388                    start: TxPos {
1389                        base: 42,
1390                        offset: Some(-10),
1391                    },
1392                    end: TxPos {
1393                        base: 42,
1394                        offset: Some(10),
1395                    }
1396                }
1397            ),
1398            "42-10_42+10".to_string(),
1399        );
1400
1401        assert_eq!(
1402            format!(
1403                "{}",
1404                TxInterval {
1405                    start: TxPos {
1406                        base: 42,
1407                        offset: Some(10),
1408                    },
1409                    end: TxPos {
1410                        base: 42,
1411                        offset: Some(10),
1412                    }
1413                }
1414            ),
1415            "42+10".to_string(),
1416        );
1417    }
1418
1419    #[test]
1420    fn tx_loc_edit() {
1421        assert_eq!(
1422            format!(
1423                "{}",
1424                TxLocEdit {
1425                    loc: Mu::Certain(TxInterval {
1426                        start: TxPos {
1427                            base: 42,
1428                            offset: Some(-10),
1429                        },
1430                        end: TxPos {
1431                            base: 42,
1432                            offset: Some(10),
1433                        }
1434                    }),
1435                    edit: Mu::Certain(NaEdit::RefAlt {
1436                        reference: "".to_string(),
1437                        alternative: "".to_string()
1438                    })
1439                }
1440            ),
1441            "42-10_42+10=".to_string(),
1442        );
1443    }
1444    #[test]
1445    fn rna_pos() {
1446        assert_eq!(
1447            format!(
1448                "{}",
1449                RnaPos {
1450                    base: 42,
1451                    offset: None,
1452                }
1453            ),
1454            "42".to_string(),
1455        );
1456
1457        assert_eq!(
1458            format!(
1459                "{}",
1460                RnaPos {
1461                    base: 42,
1462                    offset: Some(10),
1463                }
1464            ),
1465            "42+10".to_string(),
1466        );
1467
1468        assert_eq!(
1469            format!(
1470                "{}",
1471                RnaPos {
1472                    base: 42,
1473                    offset: Some(-10),
1474                }
1475            ),
1476            "42-10".to_string(),
1477        );
1478    }
1479
1480    #[test]
1481    fn rna_interval() {
1482        assert_eq!(
1483            format!(
1484                "{}",
1485                RnaInterval {
1486                    start: RnaPos {
1487                        base: 42,
1488                        offset: Some(-10),
1489                    },
1490                    end: RnaPos {
1491                        base: 42,
1492                        offset: Some(10),
1493                    }
1494                }
1495            ),
1496            "42-10_42+10".to_string(),
1497        );
1498
1499        assert_eq!(
1500            format!(
1501                "{}",
1502                RnaInterval {
1503                    start: RnaPos {
1504                        base: 42,
1505                        offset: Some(10),
1506                    },
1507                    end: RnaPos {
1508                        base: 42,
1509                        offset: Some(10),
1510                    }
1511                }
1512            ),
1513            "42+10".to_string(),
1514        );
1515    }
1516
1517    #[test]
1518    fn rna_loc_edit() {
1519        assert_eq!(
1520            format!(
1521                "{}",
1522                RnaLocEdit {
1523                    loc: Mu::Certain(RnaInterval {
1524                        start: RnaPos {
1525                            base: 42,
1526                            offset: Some(-10),
1527                        },
1528                        end: RnaPos {
1529                            base: 42,
1530                            offset: Some(10),
1531                        }
1532                    }),
1533                    edit: Mu::Certain(NaEdit::RefAlt {
1534                        reference: "".to_string(),
1535                        alternative: "".to_string()
1536                    })
1537                }
1538            ),
1539            "42-10_42+10=".to_string(),
1540        );
1541    }
1542
1543    #[test]
1544    fn genome_interval() {
1545        assert_eq!(
1546            format!(
1547                "{}",
1548                GenomeInterval {
1549                    start: None,
1550                    end: None
1551                }
1552            ),
1553            "?".to_string(),
1554        );
1555
1556        assert_eq!(
1557            format!(
1558                "{}",
1559                GenomeInterval {
1560                    start: Some(10),
1561                    end: None
1562                }
1563            ),
1564            "10_?".to_string(),
1565        );
1566
1567        assert_eq!(
1568            format!(
1569                "{}",
1570                GenomeInterval {
1571                    start: None,
1572                    end: Some(10)
1573                }
1574            ),
1575            "?_10".to_string(),
1576        );
1577
1578        assert_eq!(
1579            format!(
1580                "{}",
1581                GenomeInterval {
1582                    start: Some(10),
1583                    end: Some(20)
1584                }
1585            ),
1586            "10_20".to_string(),
1587        );
1588
1589        assert_eq!(
1590            format!(
1591                "{}",
1592                GenomeInterval {
1593                    start: Some(10),
1594                    end: Some(10)
1595                }
1596            ),
1597            "10".to_string(),
1598        );
1599    }
1600
1601    #[test]
1602    fn genome_loc_edit() {
1603        assert_eq!(
1604            format!(
1605                "{}",
1606                GenomeLocEdit {
1607                    loc: Mu::Certain(GenomeInterval {
1608                        start: Some(10),
1609                        end: Some(20)
1610                    }),
1611                    edit: Mu::Certain(NaEdit::RefAlt {
1612                        reference: "C".to_string(),
1613                        alternative: "T".to_string()
1614                    })
1615                }
1616            ),
1617            "10_20C>T".to_string(),
1618        );
1619    }
1620
1621    #[test]
1622    fn mt_interval() {
1623        assert_eq!(
1624            format!(
1625                "{}",
1626                MtInterval {
1627                    start: None,
1628                    end: None
1629                }
1630            ),
1631            "?".to_string(),
1632        );
1633
1634        assert_eq!(
1635            format!(
1636                "{}",
1637                MtInterval {
1638                    start: Some(10),
1639                    end: None
1640                }
1641            ),
1642            "10_?".to_string(),
1643        );
1644
1645        assert_eq!(
1646            format!(
1647                "{}",
1648                MtInterval {
1649                    start: None,
1650                    end: Some(10)
1651                }
1652            ),
1653            "?_10".to_string(),
1654        );
1655
1656        assert_eq!(
1657            format!(
1658                "{}",
1659                MtInterval {
1660                    start: Some(10),
1661                    end: Some(20)
1662                }
1663            ),
1664            "10_20".to_string(),
1665        );
1666
1667        assert_eq!(
1668            format!(
1669                "{}",
1670                MtInterval {
1671                    start: Some(10),
1672                    end: Some(10)
1673                }
1674            ),
1675            "10".to_string(),
1676        );
1677    }
1678
1679    #[test]
1680    fn mt_loc_edit() {
1681        assert_eq!(
1682            format!(
1683                "{}",
1684                MtLocEdit {
1685                    loc: Mu::Certain(MtInterval {
1686                        start: Some(10),
1687                        end: Some(20)
1688                    }),
1689                    edit: Mu::Certain(NaEdit::RefAlt {
1690                        reference: "C".to_string(),
1691                        alternative: "T".to_string()
1692                    })
1693                }
1694            ),
1695            "10_20C>T".to_string(),
1696        );
1697    }
1698
1699    #[test]
1700    fn prot_pos() {
1701        assert_eq!(
1702            format!(
1703                "{}",
1704                ProtPos {
1705                    aa: "Leu".to_string(),
1706                    number: 42
1707                }
1708            ),
1709            "Leu42".to_string()
1710        );
1711    }
1712
1713    #[test]
1714    fn prot_interval() {
1715        assert_eq!(
1716            format!(
1717                "{}",
1718                ProtInterval {
1719                    start: ProtPos {
1720                        aa: "Leu".to_string(),
1721                        number: 42
1722                    },
1723                    end: ProtPos {
1724                        aa: "Thr".to_string(),
1725                        number: 43
1726                    },
1727                }
1728            ),
1729            "Leu42_Thr43".to_string()
1730        );
1731
1732        assert_eq!(
1733            format!(
1734                "{}",
1735                ProtInterval {
1736                    start: ProtPos {
1737                        aa: "Leu".to_string(),
1738                        number: 42
1739                    },
1740                    end: ProtPos {
1741                        aa: "Leu".to_string(),
1742                        number: 42
1743                    },
1744                }
1745            ),
1746            "Leu42".to_string()
1747        );
1748    }
1749
1750    #[test]
1751    fn prot_loc_edit() {
1752        assert_eq!(
1753            format!(
1754                "{}",
1755                ProtLocEdit::Ordinary {
1756                    loc: Mu::Certain(ProtInterval {
1757                        start: ProtPos {
1758                            aa: "Leu".to_string(),
1759                            number: 42
1760                        },
1761                        end: ProtPos {
1762                            aa: "Thr".to_string(),
1763                            number: 43
1764                        },
1765                    },),
1766                    edit: Mu::Certain(ProteinEdit::Ident),
1767                }
1768            ),
1769            "Leu42_Thr43=".to_string()
1770        );
1771
1772        assert_eq!(format!("{}", ProtLocEdit::NoChange,), "=".to_string());
1773
1774        assert_eq!(
1775            format!("{}", ProtLocEdit::NoChangeUncertain,),
1776            "(=)".to_string()
1777        );
1778
1779        assert_eq!(format!("{}", ProtLocEdit::NoProtein,), "0".to_string());
1780
1781        assert_eq!(
1782            format!("{}", ProtLocEdit::NoProteinUncertain,),
1783            "0?".to_string()
1784        );
1785    }
1786
1787    #[test]
1788    fn hgvs_variant_cds() {
1789        assert_eq!(
1790            format!(
1791                "{}",
1792                HgvsVariant::CdsVariant {
1793                    accession: Accession {
1794                        value: "NA12345.1".to_string()
1795                    },
1796                    gene_symbol: Some(GeneSymbol {
1797                        value: "TTN".to_string()
1798                    }),
1799                    loc_edit: CdsLocEdit {
1800                        loc: Mu::Certain(CdsInterval {
1801                            start: CdsPos {
1802                                base: 100,
1803                                offset: None,
1804                                cds_from: CdsFrom::Start,
1805                            },
1806                            end: CdsPos {
1807                                base: 100,
1808                                offset: None,
1809                                cds_from: CdsFrom::Start,
1810                            }
1811                        }),
1812                        edit: Mu::Certain(NaEdit::RefAlt {
1813                            reference: "C".to_string(),
1814                            alternative: "T".to_string()
1815                        })
1816                    }
1817                }
1818            ),
1819            "NA12345.1(TTN):c.100C>T".to_string(),
1820        );
1821
1822        assert_eq!(
1823            format!(
1824                "{}",
1825                HgvsVariant::CdsVariant {
1826                    accession: Accession {
1827                        value: "NA12345.1".to_string()
1828                    },
1829                    gene_symbol: None,
1830                    loc_edit: CdsLocEdit {
1831                        loc: Mu::Certain(CdsInterval {
1832                            start: CdsPos {
1833                                base: 100,
1834                                offset: None,
1835                                cds_from: CdsFrom::Start,
1836                            },
1837                            end: CdsPos {
1838                                base: 100,
1839                                offset: None,
1840                                cds_from: CdsFrom::Start,
1841                            }
1842                        }),
1843                        edit: Mu::Certain(NaEdit::RefAlt {
1844                            reference: "C".to_string(),
1845                            alternative: "T".to_string()
1846                        })
1847                    }
1848                }
1849            ),
1850            "NA12345.1:c.100C>T".to_string(),
1851        );
1852    }
1853
1854    #[test]
1855    fn hgvs_variant_genome() {
1856        assert_eq!(
1857            format!(
1858                "{}",
1859                HgvsVariant::GenomeVariant {
1860                    accession: Accession {
1861                        value: "NA12345.1".to_string()
1862                    },
1863                    gene_symbol: Some(GeneSymbol {
1864                        value: "TTN".to_string()
1865                    }),
1866                    loc_edit: GenomeLocEdit {
1867                        loc: Mu::Certain(GenomeInterval {
1868                            start: Some(100),
1869                            end: Some(100)
1870                        }),
1871                        edit: Mu::Certain(NaEdit::RefAlt {
1872                            reference: "C".to_string(),
1873                            alternative: "T".to_string()
1874                        })
1875                    }
1876                }
1877            ),
1878            "NA12345.1(TTN):g.100C>T".to_string(),
1879        );
1880
1881        assert_eq!(
1882            format!(
1883                "{}",
1884                HgvsVariant::GenomeVariant {
1885                    accession: Accession {
1886                        value: "NA12345.1".to_string()
1887                    },
1888                    gene_symbol: None,
1889                    loc_edit: GenomeLocEdit {
1890                        loc: Mu::Certain(GenomeInterval {
1891                            start: Some(100),
1892                            end: Some(100)
1893                        }),
1894                        edit: Mu::Certain(NaEdit::RefAlt {
1895                            reference: "C".to_string(),
1896                            alternative: "T".to_string()
1897                        })
1898                    }
1899                }
1900            ),
1901            "NA12345.1:g.100C>T".to_string(),
1902        );
1903    }
1904
1905    #[test]
1906    fn hgvs_variant_mt() {
1907        assert_eq!(
1908            format!(
1909                "{}",
1910                HgvsVariant::MtVariant {
1911                    accession: Accession {
1912                        value: "NA12345.1".to_string()
1913                    },
1914                    gene_symbol: Some(GeneSymbol {
1915                        value: "TTN".to_string()
1916                    }),
1917                    loc_edit: MtLocEdit {
1918                        loc: Mu::Certain(MtInterval {
1919                            start: Some(100),
1920                            end: Some(100)
1921                        }),
1922                        edit: Mu::Certain(NaEdit::RefAlt {
1923                            reference: "C".to_string(),
1924                            alternative: "T".to_string()
1925                        })
1926                    }
1927                }
1928            ),
1929            "NA12345.1(TTN):m.100C>T".to_string(),
1930        );
1931
1932        assert_eq!(
1933            format!(
1934                "{}",
1935                HgvsVariant::MtVariant {
1936                    accession: Accession {
1937                        value: "NA12345.1".to_string()
1938                    },
1939                    gene_symbol: None,
1940                    loc_edit: MtLocEdit {
1941                        loc: Mu::Certain(MtInterval {
1942                            start: Some(100),
1943                            end: Some(100)
1944                        }),
1945                        edit: Mu::Certain(NaEdit::RefAlt {
1946                            reference: "C".to_string(),
1947                            alternative: "T".to_string()
1948                        })
1949                    }
1950                }
1951            ),
1952            "NA12345.1:m.100C>T".to_string(),
1953        );
1954    }
1955
1956    #[test]
1957    fn hgvs_variant_tx() {
1958        assert_eq!(
1959            format!(
1960                "{}",
1961                HgvsVariant::TxVariant {
1962                    accession: Accession {
1963                        value: "NA12345.1".to_string()
1964                    },
1965                    gene_symbol: Some(GeneSymbol {
1966                        value: "TTN".to_string()
1967                    }),
1968                    loc_edit: TxLocEdit {
1969                        loc: Mu::Certain(TxInterval {
1970                            start: TxPos {
1971                                base: 100,
1972                                offset: None
1973                            },
1974                            end: TxPos {
1975                                base: 100,
1976                                offset: None
1977                            },
1978                        }),
1979                        edit: Mu::Certain(NaEdit::RefAlt {
1980                            reference: "C".to_string(),
1981                            alternative: "T".to_string()
1982                        })
1983                    }
1984                }
1985            ),
1986            "NA12345.1(TTN):n.100C>T".to_string(),
1987        );
1988
1989        assert_eq!(
1990            format!(
1991                "{}",
1992                HgvsVariant::TxVariant {
1993                    accession: Accession {
1994                        value: "NA12345.1".to_string()
1995                    },
1996                    gene_symbol: None,
1997                    loc_edit: TxLocEdit {
1998                        loc: Mu::Certain(TxInterval {
1999                            start: TxPos {
2000                                base: 100,
2001                                offset: None
2002                            },
2003                            end: TxPos {
2004                                base: 100,
2005                                offset: None
2006                            },
2007                        }),
2008                        edit: Mu::Certain(NaEdit::RefAlt {
2009                            reference: "C".to_string(),
2010                            alternative: "T".to_string()
2011                        })
2012                    }
2013                }
2014            ),
2015            "NA12345.1:n.100C>T".to_string(),
2016        );
2017    }
2018
2019    #[test]
2020    fn hgvs_variant_rna() {
2021        assert_eq!(
2022            format!(
2023                "{}",
2024                HgvsVariant::RnaVariant {
2025                    accession: Accession {
2026                        value: "NA12345.1".to_string()
2027                    },
2028                    gene_symbol: Some(GeneSymbol {
2029                        value: "TTN".to_string()
2030                    }),
2031                    loc_edit: RnaLocEdit {
2032                        loc: Mu::Certain(RnaInterval {
2033                            start: RnaPos {
2034                                base: 100,
2035                                offset: None
2036                            },
2037                            end: RnaPos {
2038                                base: 100,
2039                                offset: None
2040                            },
2041                        }),
2042                        edit: Mu::Certain(NaEdit::RefAlt {
2043                            reference: "C".to_string(),
2044                            alternative: "T".to_string()
2045                        })
2046                    }
2047                }
2048            ),
2049            "NA12345.1(TTN):r.100C>T".to_string(),
2050        );
2051
2052        assert_eq!(
2053            format!(
2054                "{}",
2055                HgvsVariant::RnaVariant {
2056                    accession: Accession {
2057                        value: "NA12345.1".to_string()
2058                    },
2059                    gene_symbol: None,
2060                    loc_edit: RnaLocEdit {
2061                        loc: Mu::Certain(RnaInterval {
2062                            start: RnaPos {
2063                                base: 100,
2064                                offset: None
2065                            },
2066                            end: RnaPos {
2067                                base: 100,
2068                                offset: None
2069                            },
2070                        }),
2071                        edit: Mu::Certain(NaEdit::RefAlt {
2072                            reference: "C".to_string(),
2073                            alternative: "T".to_string()
2074                        })
2075                    }
2076                }
2077            ),
2078            "NA12345.1:r.100C>T".to_string(),
2079        );
2080    }
2081
2082    #[test]
2083    fn hgvs_variant_prot() {
2084        assert_eq!(
2085            format!(
2086                "{}",
2087                HgvsVariant::ProtVariant {
2088                    accession: Accession {
2089                        value: "NA12345.1".to_string()
2090                    },
2091                    gene_symbol: Some(GeneSymbol {
2092                        value: "TTN".to_string()
2093                    }),
2094                    loc_edit: ProtLocEdit::NoChange
2095                }
2096            ),
2097            "NA12345.1(TTN):p.=".to_string(),
2098        );
2099    }
2100
2101    // This test uses the "gauntlet" file from the hgvs package for round-tripping.
2102    #[test]
2103    fn roundtrip_hgvs_gauntlet() -> Result<(), Error> {
2104        let reader = BufReader::new(File::open("tests/data/parser/gauntlet")?);
2105
2106        for line in reader.lines() {
2107            let line = line?;
2108            let line = line.trim();
2109            if !line.starts_with('#') && !line.is_empty() {
2110                let hgvs_variant = HgvsVariant::from_str(line)?;
2111                let hgvs_str = format!("{}", &hgvs_variant);
2112                assert_eq!(
2113                    hgvs_str, line,
2114                    "round-trip failed for variant {:?}; line= {}",
2115                    &hgvs_variant, line
2116                );
2117            }
2118        }
2119
2120        Ok(())
2121    }
2122}
2123
2124// <LICENSE>
2125// Copyright 2023 hgvs-rs Contributors
2126// Copyright 2014 Bioutils Contributors
2127//
2128// Licensed under the Apache License, Version 2.0 (the "License");
2129// you may not use this file except in compliance with the License.
2130// You may obtain a copy of the License at
2131//
2132//     http://www.apache.org/licenses/LICENSE-2.0
2133//
2134// Unless required by applicable law or agreed to in writing, software
2135// distributed under the License is distributed on an "AS IS" BASIS,
2136// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2137// See the License for the specific language governing permissions and
2138// limitations under the License.
2139// </LICENSE>