noodles_vcf/io/reader/
record_buf.rs1mod alternate_bases;
4mod filters;
5mod ids;
6mod info;
7mod position;
8mod quality_score;
9mod reference_bases;
10mod reference_sequence_name;
11mod samples;
12pub(crate) mod value;
13
14use std::{error, fmt};
15
16use self::{
17 alternate_bases::parse_alternate_bases, filters::parse_filters, ids::parse_ids,
18 info::parse_info, position::parse_position, quality_score::parse_quality_score,
19 reference_bases::parse_reference_bases, reference_sequence_name::parse_reference_sequence_name,
20 samples::parse_samples,
21};
22use crate::{variant::RecordBuf, Header};
23
24const MISSING: &str = ".";
25
26#[allow(clippy::enum_variant_names)]
28#[derive(Clone, Debug, Eq, PartialEq)]
29pub enum ParseError {
30 InvalidPosition(position::ParseError),
32 InvalidIds(ids::ParseError),
34 InvalidReferenceBases(reference_bases::ParseError),
36 InvalidAlternateBases(alternate_bases::ParseError),
38 InvalidQualityScore(quality_score::ParseError),
40 InvalidFilters(filters::ParseError),
42 InvalidInfo(info::ParseError),
44 InvalidSamples(samples::ParseError),
46}
47
48impl error::Error for ParseError {
49 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
50 match self {
51 Self::InvalidPosition(e) => Some(e),
52 Self::InvalidIds(e) => Some(e),
53 Self::InvalidReferenceBases(e) => Some(e),
54 Self::InvalidAlternateBases(e) => Some(e),
55 Self::InvalidQualityScore(e) => Some(e),
56 Self::InvalidFilters(e) => Some(e),
57 Self::InvalidInfo(e) => Some(e),
58 Self::InvalidSamples(e) => Some(e),
59 }
60 }
61}
62
63impl fmt::Display for ParseError {
64 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65 match self {
66 Self::InvalidPosition(_) => write!(f, "invalid position"),
67 Self::InvalidIds(_) => write!(f, "invalid IDs"),
68 Self::InvalidReferenceBases(_) => write!(f, "invalid reference bases"),
69 Self::InvalidAlternateBases(_) => write!(f, "invalid alternate bases"),
70 Self::InvalidQualityScore(_) => write!(f, "invalid quality score"),
71 Self::InvalidFilters(_) => write!(f, "invalid filters"),
72 Self::InvalidInfo(_) => write!(f, "invalid info"),
73 Self::InvalidSamples(_) => write!(f, "invalid samples"),
74 }
75 }
76}
77
78pub(crate) fn parse_record_buf(
79 mut s: &str,
80 header: &Header,
81 record: &mut RecordBuf,
82) -> Result<(), ParseError> {
83 let field = next_field(&mut s);
84 parse_reference_sequence_name(field, record.reference_sequence_name_mut());
85
86 let field = next_field(&mut s);
87 *record.variant_start_mut() = parse_position(field).map_err(ParseError::InvalidPosition)?;
88
89 record.ids_mut().as_mut().clear();
90 let field = next_field(&mut s);
91 if field != MISSING {
92 parse_ids(field, record.ids_mut()).map_err(ParseError::InvalidIds)?;
93 }
94
95 let field = next_field(&mut s);
96 parse_reference_bases(field, record.reference_bases_mut())
97 .map_err(ParseError::InvalidReferenceBases)?;
98
99 record.alternate_bases_mut().as_mut().clear();
100 let field = next_field(&mut s);
101 if field != MISSING {
102 parse_alternate_bases(field, record.alternate_bases_mut())
103 .map_err(ParseError::InvalidAlternateBases)?;
104 }
105
106 let field = next_field(&mut s);
107 *record.quality_score_mut() = match field {
108 MISSING => None,
109 _ => parse_quality_score(field)
110 .map(Some)
111 .map_err(ParseError::InvalidQualityScore)?,
112 };
113
114 let field = next_field(&mut s);
115 match field {
116 MISSING => record.filters_mut().as_mut().clear(),
117 _ => parse_filters(field, record.filters_mut()).map_err(ParseError::InvalidFilters)?,
118 }
119
120 record.info_mut().clear();
121 let field = next_field(&mut s);
122 if field != MISSING {
123 parse_info(header, field, record.info_mut()).map_err(ParseError::InvalidInfo)?;
124 }
125
126 parse_samples(header, s, record.samples_mut()).map_err(ParseError::InvalidSamples)?;
127
128 Ok(())
129}
130
131fn next_field<'a>(s: &mut &'a str) -> &'a str {
132 const DELIMITER: char = '\t';
133
134 let (field, rest) = s
135 .split_once(DELIMITER)
136 .unwrap_or_else(|| s.split_at(s.len()));
137
138 *s = rest;
139
140 field
141}