noodles_vcf/
header.rs

1//! VCF header and fields.
2
3mod builder;
4pub mod file_format;
5pub mod parser;
6pub mod record;
7pub mod string_maps;
8
9pub use self::{
10    builder::Builder, file_format::FileFormat, parser::ParseError, parser::Parser, record::Record,
11    string_maps::StringMaps,
12};
13
14use std::{hash::Hash, str::FromStr};
15
16use indexmap::{IndexMap, IndexSet};
17
18use self::record::value::{
19    map::{AlternativeAllele, Contig, Filter, Format, Info},
20    Map,
21};
22
23/// VCF header info records.
24pub type Infos = IndexMap<String, Map<Info>>;
25
26/// VCF header filter records.
27pub type Filters = IndexMap<String, Map<Filter>>;
28
29/// VCF header format records.
30pub type Formats = IndexMap<String, Map<Format>>;
31
32/// VCF header alternative allele records.
33pub type AlternativeAlleles = IndexMap<String, Map<AlternativeAllele>>;
34
35/// VCF header contig records.
36pub type Contigs = IndexMap<String, Map<Contig>>;
37
38/// VCF header sample names.
39pub type SampleNames = IndexSet<String>;
40
41/// VCF header generic records.
42pub type OtherRecords = IndexMap<record::key::Other, record::value::Collection>;
43
44/// A VCF header.
45#[derive(Clone, Debug, Eq, PartialEq)]
46pub struct Header {
47    file_format: FileFormat,
48    infos: Infos,
49    filters: Filters,
50    formats: Formats,
51    alternative_alleles: AlternativeAlleles,
52    contigs: Contigs,
53    sample_names: SampleNames,
54    other_records: OtherRecords,
55    string_maps: StringMaps,
56}
57
58impl Header {
59    /// Returns a builder to create a record from each of its fields.
60    ///
61    /// # Examples
62    ///
63    /// ```
64    /// use noodles_vcf as vcf;
65    /// let builder = vcf::Header::builder();
66    /// ```
67    pub fn builder() -> Builder {
68        Builder::default()
69    }
70
71    /// Returns the file format (`fileformat`) of the VCF.
72    ///
73    /// `fileformat` is a required meta record and is guaranteed to be set.
74    ///
75    /// # Examples
76    ///
77    /// ```
78    /// use noodles_vcf::{self as vcf, header::FileFormat};
79    ///
80    /// let header = vcf::Header::builder()
81    ///     .set_file_format(FileFormat::default())
82    ///     .build();
83    ///
84    /// assert_eq!(header.file_format(), FileFormat::default());
85    /// ```
86    pub fn file_format(&self) -> FileFormat {
87        self.file_format
88    }
89
90    /// Returns a mutable reference to the file format (`fileformat`) of the VCF.
91    ///
92    /// `fileformat` is a required meta record and is guaranteed to be set.
93    ///
94    /// # Examples
95    ///
96    /// ```
97    /// use noodles_vcf::{self as vcf, header::FileFormat};
98    ///
99    /// let mut header = vcf::Header::default();
100    ///
101    /// let file_format = FileFormat::new(4, 2);
102    /// *header.file_format_mut() = file_format;
103    ///
104    /// assert_eq!(header.file_format(), file_format);
105    /// ```
106    pub fn file_format_mut(&mut self) -> &mut FileFormat {
107        &mut self.file_format
108    }
109
110    /// Returns a map of information records (`INFO`).
111    ///
112    /// # Examples
113    ///
114    /// ```
115    /// use noodles_vcf::{
116    ///     self as vcf,
117    ///     header::record::value::{map::Info, Map},
118    ///     variant::record::info::field::key,
119    /// };
120    ///
121    /// let id = key::SAMPLES_WITH_DATA_COUNT;
122    /// let info = Map::<Info>::from(id);
123    ///
124    /// let header = vcf::Header::builder()
125    ///     .add_info(id, info.clone())
126    ///     .build();
127    ///
128    /// let infos = header.infos();
129    /// assert_eq!(infos.len(), 1);
130    /// assert_eq!(&infos[0], &info);
131    /// ```
132    pub fn infos(&self) -> &Infos {
133        &self.infos
134    }
135
136    /// Returns a mutable reference to a map of information records (`INFO`).
137    ///
138    /// # Examples
139    ///
140    /// ```
141    /// use noodles_vcf::{
142    ///     self as vcf,
143    ///     header::record::value::{map::Info, Map},
144    ///     variant::record::info::field::key,
145    /// };
146    ///
147    /// let mut header = vcf::Header::default();
148    ///
149    /// let id = key::SAMPLES_WITH_DATA_COUNT;
150    /// let info = Map::<Info>::from(id);
151    /// header.infos_mut().insert(id.into(), info.clone());
152    ///
153    /// let infos = header.infos();
154    /// assert_eq!(infos.len(), 1);
155    /// assert_eq!(&infos[0], &info);
156    /// ```
157    pub fn infos_mut(&mut self) -> &mut Infos {
158        &mut self.infos
159    }
160
161    /// Returns a map of filter records (`FILTER`).
162    ///
163    /// # Examples
164    ///
165    /// ```
166    /// use noodles_vcf::{self as vcf, header::record::value::{map::Filter, Map}};
167    ///
168    /// let filter = Map::<Filter>::new("Quality below 10");
169    ///
170    /// let header = vcf::Header::builder()
171    ///     .add_filter("q10", filter.clone())
172    ///     .build();
173    ///
174    /// let filters = header.filters();
175    /// assert_eq!(filters.len(), 1);
176    /// assert_eq!(&filters[0], &filter);
177    /// ```
178    pub fn filters(&self) -> &Filters {
179        &self.filters
180    }
181
182    /// Returns a mutable reference to a map of filter records (`FILTER`).
183    ///
184    /// # Examples
185    ///
186    /// ```
187    /// use noodles_vcf::{self as vcf, header::record::value::{map::Filter, Map}};
188    ///
189    /// let mut header = vcf::Header::default();
190    ///
191    /// let filter = Map::<Filter>::new("Quality below 10");
192    /// header.filters_mut().insert(String::from("q10"), filter.clone());
193    ///
194    /// let filters = header.filters();
195    /// assert_eq!(filters.len(), 1);
196    /// assert_eq!(&filters[0], &filter);
197    /// ```
198    pub fn filters_mut(&mut self) -> &mut Filters {
199        &mut self.filters
200    }
201
202    /// Returns a list of genotype format records (`FORMAT`).
203    ///
204    /// # Examples
205    ///
206    /// ```
207    /// use noodles_vcf::{
208    ///     self as vcf,
209    ///     header::record::value::{map::Format, Map},
210    ///     variant::record::samples::keys::key,
211    /// };
212    ///
213    /// let id = key::GENOTYPE;
214    /// let format = Map::<Format>::from(id);
215    ///
216    /// let header = vcf::Header::builder()
217    ///     .add_format(id, format.clone())
218    ///     .build();
219    ///
220    /// let formats = header.formats();
221    /// assert_eq!(formats.len(), 1);
222    /// assert_eq!(&formats[0], &format);
223    /// ```
224    pub fn formats(&self) -> &Formats {
225        &self.formats
226    }
227
228    /// Returns a mutable reference to a list of genotype format records (`FORMAT`).
229    ///
230    /// # Examples
231    ///
232    /// ```
233    /// use noodles_vcf::{
234    ///     self as vcf,
235    ///     header::record::value::{map::Format, Map},
236    ///     variant::record::samples::keys::key,
237    /// };
238    ///
239    /// let mut header = vcf::Header::default();
240    ///
241    /// let id = key::GENOTYPE;
242    /// let format = Map::<Format>::from(id);
243    /// header.formats_mut().insert(id.into(), format.clone());
244    ///
245    /// let formats = header.formats();
246    /// assert_eq!(formats.len(), 1);
247    /// assert_eq!(&formats[0], &format);
248    /// ```
249    pub fn formats_mut(&mut self) -> &mut Formats {
250        &mut self.formats
251    }
252
253    /// Returns a map of symbolic alternate alleles (`ALT`).
254    ///
255    /// # Examples
256    ///
257    /// ```
258    /// use noodles_vcf::{
259    ///     self as vcf,
260    ///     header::record::value::{map::AlternativeAllele, Map},
261    /// };
262    ///
263    /// let alt = Map::<AlternativeAllele>::new("Deletion");
264    ///
265    /// let header = vcf::Header::builder()
266    ///     .add_alternative_allele("DEL", alt.clone())
267    ///     .build();
268    ///
269    /// let alternative_alleles = header.alternative_alleles();
270    /// assert_eq!(alternative_alleles.len(), 1);
271    /// assert_eq!(&alternative_alleles[0], &alt);
272    /// ```
273    pub fn alternative_alleles(&self) -> &AlternativeAlleles {
274        &self.alternative_alleles
275    }
276
277    /// Returns a mutable reference to a map of symbolic alternate alleles (`ALT`).
278    ///
279    /// # Examples
280    ///
281    /// ```
282    /// use noodles_vcf::{
283    ///     self as vcf,
284    ///     header::record::value::{map::AlternativeAllele, Map},
285    /// };
286    ///
287    /// let mut header = vcf::Header::default();
288    ///
289    /// let alt = Map::<AlternativeAllele>::new("Deletion");
290    /// header.alternative_alleles_mut().insert(String::from("DEL"), alt.clone());
291    ///
292    /// let alternative_alleles = header.alternative_alleles();
293    /// assert_eq!(alternative_alleles.len(), 1);
294    /// assert_eq!(&alternative_alleles[0], &alt);
295    /// ```
296    pub fn alternative_alleles_mut(&mut self) -> &mut AlternativeAlleles {
297        &mut self.alternative_alleles
298    }
299
300    /// Returns a map of contig records (`contig`).
301    ///
302    /// # Examples
303    ///
304    /// ```
305    /// use noodles_vcf::{self as vcf, header::record::value::{map::Contig, Map}};
306    ///
307    /// let contig = Map::<Contig>::new();
308    ///
309    /// let header = vcf::Header::builder()
310    ///     .add_contig("sq0", contig.clone())
311    ///     .build();
312    ///
313    /// let contigs = header.contigs();
314    /// assert_eq!(contigs.len(), 1);
315    /// assert_eq!(&contigs[0], &contig);
316    /// ```
317    pub fn contigs(&self) -> &Contigs {
318        &self.contigs
319    }
320
321    /// Returns a mutable reference to a map of contig records (`contig`).
322    ///
323    /// # Examples
324    ///
325    /// ```
326    /// use noodles_vcf::{self as vcf, header::record::value::{map::Contig, Map}};
327    ///
328    /// let mut header = vcf::Header::default();
329    ///
330    /// let contig = Map::<Contig>::new();
331    /// header.contigs_mut().insert(String::from("sq0"), contig.clone());
332    ///
333    /// let contigs = header.contigs();
334    /// assert_eq!(contigs.len(), 1);
335    /// assert_eq!(&contigs[0], &contig);
336    /// ```
337    pub fn contigs_mut(&mut self) -> &mut Contigs {
338        &mut self.contigs
339    }
340
341    /// Returns a list of sample names that come after the FORMAT column in the header record.
342    ///
343    /// # Examples
344    ///
345    /// ```
346    /// use indexmap::IndexSet;
347    /// use noodles_vcf as vcf;
348    ///
349    /// let header = vcf::Header::builder()
350    ///     .add_sample_name("sample0")
351    ///     .add_sample_name("sample1")
352    ///     .build();
353    ///
354    /// let expected: IndexSet<_> = [String::from("sample0"), String::from("sample1")]
355    ///     .into_iter()
356    ///     .collect();
357    ///
358    /// assert_eq!(header.sample_names(), &expected);
359    /// ```
360    pub fn sample_names(&self) -> &SampleNames {
361        &self.sample_names
362    }
363
364    /// Returns a mutable reference to a list of sample names that come after the FORMAT column in
365    /// the header record.
366    ///
367    /// # Examples
368    ///
369    /// ```
370    /// use indexmap::IndexSet;
371    /// use noodles_vcf as vcf;
372    ///
373    /// let mut header = vcf::Header::builder().add_sample_name("sample0").build();
374    /// header.sample_names_mut().insert(String::from("sample1"));
375    ///
376    /// let expected: IndexSet<_> = [String::from("sample0"), String::from("sample1")]
377    ///     .into_iter()
378    ///     .collect();
379    ///
380    /// assert_eq!(header.sample_names(), &expected);
381    /// ```
382    pub fn sample_names_mut(&mut self) -> &mut SampleNames {
383        &mut self.sample_names
384    }
385
386    /// Returns a map of records with nonstandard keys.
387    ///
388    /// This includes all records other than `fileformat`, `INFO`, `FILTER`, `FORMAT`, `ALT`, and
389    /// `contig`.
390    ///
391    /// # Examples
392    ///
393    /// ```
394    /// use noodles_vcf::{self as vcf, header::record::Value};
395    ///
396    /// let header = vcf::Header::builder()
397    ///     .insert("fileDate".parse()?, Value::from("20200709"))?
398    ///     .build();
399    ///
400    /// assert_eq!(header.other_records().len(), 1);
401    /// # Ok::<_, Box<dyn std::error::Error>>(())
402    /// ```
403    pub fn other_records(&self) -> &OtherRecords {
404        &self.other_records
405    }
406
407    /// Returns a mutable reference to a map of collections of records with nonstandard keys.
408    ///
409    /// This includes all records other than `fileformat`, `INFO`, `FILTER`, `FORMAT`, `ALT`, and
410    /// `contig`.
411    ///
412    /// To simply add an nonstandard record, consider using [`Self::insert`] instead.
413    ///
414    /// # Examples
415    ///
416    /// ```
417    /// use noodles_vcf::{
418    ///     self as vcf,
419    ///     header::record::{value::Collection, Value},
420    /// };
421    ///
422    /// let mut header = vcf::Header::default();
423    ///
424    /// let collection = Collection::Unstructured(vec![String::from("20200709")]);
425    /// header.other_records_mut().insert("fileDate".parse()?, collection.clone());
426    ///
427    /// assert_eq!(header.other_records().get("fileDate"), Some(&collection));
428    /// # Ok::<_, vcf::header::record::key::other::ParseError>(())
429    /// ```
430    pub fn other_records_mut(&mut self) -> &mut OtherRecords {
431        &mut self.other_records
432    }
433
434    /// Returns a collection of header values with the given key.
435    ///
436    /// This includes all records other than `fileformat`, `INFO`, `FILTER`, `FORMAT`, `ALT`, and
437    /// `contig`.
438    ///
439    /// # Examples
440    ///
441    /// ```
442    /// use noodles_vcf::{
443    ///     self as vcf,
444    ///     header::record::{value::Collection, Value},
445    /// };
446    ///
447    /// let header = vcf::Header::builder()
448    ///     .insert("fileDate".parse()?, Value::from("20200709"))?
449    ///     .build();
450    ///
451    /// assert_eq!(
452    ///     header.get("fileDate"),
453    ///     Some(&Collection::Unstructured(vec![String::from("20200709")]))
454    /// );
455    ///
456    /// assert!(header.get("reference").is_none());
457    /// # Ok::<_, Box<dyn std::error::Error>>(())
458    /// ```
459    pub fn get<Q>(&self, key: &Q) -> Option<&record::value::Collection>
460    where
461        Q: ?Sized + Hash + indexmap::Equivalent<record::key::Other>,
462    {
463        self.other_records.get(key)
464    }
465
466    /// Inserts a key-value pair representing a nonstandard record into the header.
467    ///
468    /// # Examples
469    ///
470    /// ```
471    /// use noodles_vcf::{
472    ///     self as vcf,
473    ///     header::record::{value::Collection, Value},
474    /// };
475    ///
476    /// let mut header = vcf::Header::default();
477    /// assert!(header.get("fileDate").is_none());
478    ///
479    /// header.insert("fileDate".parse()?, Value::from("20200709"))?;
480    /// assert_eq!(
481    ///     header.get("fileDate"),
482    ///     Some(&Collection::Unstructured(vec![String::from("20200709")]))
483    /// );
484    /// # Ok::<_, Box<dyn std::error::Error>>(())
485    /// ```
486    pub fn insert(
487        &mut self,
488        key: record::key::Other,
489        value: record::Value,
490    ) -> Result<(), record::value::collection::AddError> {
491        let collection = self
492            .other_records
493            .entry(key)
494            .or_insert_with(|| match value {
495                record::Value::String(_) => record::value::Collection::Unstructured(Vec::new()),
496                record::Value::Map(..) => record::value::Collection::Structured(IndexMap::new()),
497            });
498
499        collection.add(value)
500    }
501
502    #[doc(hidden)]
503    pub fn string_maps(&self) -> &StringMaps {
504        &self.string_maps
505    }
506
507    #[doc(hidden)]
508    pub fn string_maps_mut(&mut self) -> &mut StringMaps {
509        &mut self.string_maps
510    }
511}
512
513impl Default for Header {
514    fn default() -> Self {
515        Builder::default().build()
516    }
517}
518
519impl FromStr for Header {
520    type Err = ParseError;
521
522    fn from_str(s: &str) -> Result<Self, Self::Err> {
523        Parser::default().parse(s)
524    }
525}
526
527#[cfg(test)]
528mod tests {
529    use super::*;
530
531    #[test]
532    fn test_default() {
533        let header = Header::default();
534        assert_eq!(header.file_format(), FileFormat::default());
535    }
536
537    #[test]
538    fn test_insert_with_duplicate_keys() -> Result<(), Box<dyn std::error::Error>> {
539        let key: record::key::Other = "noodles".parse()?;
540        let values = [record::Value::from("0"), record::Value::from("1")];
541
542        let mut header = Header::default();
543
544        for value in values {
545            header.insert(key.clone(), value)?;
546        }
547
548        assert_eq!(
549            header.get(&key),
550            Some(&record::value::Collection::Unstructured(vec![
551                String::from("0"),
552                String::from("1")
553            ]))
554        );
555
556        Ok(())
557    }
558}