1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
//! Lazily-evaluated VCF record and fields.

mod bounds;
mod filters;
mod genotypes;
mod ids;
mod info;

use std::fmt;

use self::bounds::Bounds;
pub use self::{filters::Filters, genotypes::Genotypes, ids::Ids, info::Info};

const MISSING: &str = ".";

/// An immutable, lazily-evaluated VCF record.
#[derive(Clone, Eq, PartialEq)]
pub struct Record {
    pub(crate) buf: String,
    pub(crate) bounds: Bounds,
}

impl Record {
    /// Returns the chromosome.
    pub fn chromosome(&self) -> &str {
        &self.buf[self.bounds.chromosome_range()]
    }

    /// Returns the position.
    pub fn position(&self) -> &str {
        &self.buf[self.bounds.position_range()]
    }

    /// Returns the IDs.
    pub fn ids(&self) -> Ids<'_> {
        let buf = &self.buf[self.bounds.ids_range()];
        Ids::new(buf)
    }

    /// Returns the reference bases.
    pub fn reference_bases(&self) -> &str {
        &self.buf[self.bounds.reference_bases_range()]
    }

    /// Returns the alternate bases.
    pub fn alternate_bases(&self) -> &str {
        &self.buf[self.bounds.alternate_bases_range()]
    }

    /// Returns the quality score.
    pub fn quality_score(&self) -> Option<&str> {
        match &self.buf[self.bounds.quality_score_range()] {
            MISSING => None,
            buf => Some(buf),
        }
    }

    /// Returns the filters.
    pub fn filters(&self) -> Option<Filters<'_>> {
        let buf = &self.buf[self.bounds.filters_range()];

        match buf {
            MISSING => None,
            _ => Some(Filters::new(buf)),
        }
    }

    /// Returns the info.
    pub fn info(&self) -> Info<'_> {
        let buf = match &self.buf[self.bounds.info_range()] {
            MISSING => "",
            buf => buf,
        };

        Info::new(buf)
    }

    /// Returns the genotypes.
    pub fn genotypes(&self) -> Genotypes<'_> {
        use crate::record::FIELD_DELIMITER;

        let buf = &self.buf[self.bounds.genotypes_range()];

        let is_missing = || {
            buf.split(FIELD_DELIMITER)
                .next()
                .map(|s| s == MISSING)
                .unwrap_or_default()
        };

        if buf.is_empty() || is_missing() {
            Genotypes::new("")
        } else {
            Genotypes::new(buf)
        }
    }
}

impl fmt::Debug for Record {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Record")
            .field("chromosome", &self.chromosome())
            .field("position", &self.position())
            .field("ids", &self.ids())
            .field("reference_bases", &self.reference_bases())
            .field("alternate_bases", &self.alternate_bases())
            .field("quality_score", &self.quality_score())
            .field("filters", &self.filters())
            .field("info", &self.info())
            .field("genotypes", &self.genotypes())
            .finish()
    }
}

impl Default for Record {
    fn default() -> Self {
        let buf = String::from("sq01.A....");

        let bounds = Bounds {
            chromosome_end: 3,
            position_end: 4,
            ids_end: 5,
            reference_bases_end: 6,
            alternate_bases_end: 7,
            quality_score_end: 8,
            filters_end: 9,
            info_end: 10,
        };

        Self { buf, bounds }
    }
}