noodles_bcf/record/samples/
series.rs

1//! BCF record samples series.
2
3pub mod value;
4
5use std::{borrow::Cow, io, mem, ops::Range, str};
6
7use noodles_vcf::{
8    self as vcf,
9    header::record::value::map::format::{self, Number},
10    variant::record::samples::{
11        keys::key,
12        series::{value::Array, Value},
13    },
14};
15
16use crate::record::value::{array::Values, read_type, read_value, Type};
17
18/// A BCF record samples series.
19pub struct Series<'r> {
20    id: usize,
21    ty: Type,
22    sample_count: usize,
23    src: &'r [u8],
24}
25
26impl<'r> Series<'r> {
27    /// Returns the name.
28    pub fn name<'h>(&self, header: &'h vcf::Header) -> io::Result<&'h str> {
29        header
30            .string_maps()
31            .strings()
32            .get_index(self.id)
33            .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "invalid string map ID"))
34    }
35
36    fn len(&self) -> usize {
37        self.sample_count
38    }
39
40    /// Returns the value at the given index.
41    pub fn get(&self, header: &vcf::Header, i: usize) -> Option<Option<io::Result<Value<'r>>>> {
42        let name = match self.name(header) {
43            Ok(name) => name,
44            Err(e) => return Some(Some(Err(e))),
45        };
46
47        if name == key::GENOTYPE {
48            match self.ty {
49                Type::Int8(len) => return get_genotype_value(self.src, header, len, i),
50                _ => todo!("unhandled type"),
51            }
52        }
53
54        let (number, ty) = header
55            .formats()
56            .get(name)
57            .map(|format| (format.number(), format.ty()))
58            .expect("missing type definition");
59
60        let value = match (number, ty, self.ty) {
61            (Number::Count(0), _, _) => todo!("invalid number for type"),
62
63            (_, _, Type::Int8(0) | Type::Int16(0) | Type::Int32(0) | Type::Float(0)) => {
64                return Some(Some(Err(io::Error::new(
65                    io::ErrorKind::InvalidData,
66                    "invalid length",
67                ))));
68            }
69
70            (Number::Count(1), format::Type::Integer, Type::Int8(len)) => {
71                get_i8_value(self.src, len, i)
72            }
73            (Number::Count(1), format::Type::Integer, Type::Int16(len)) => {
74                get_i16_value(self.src, len, i)
75            }
76            (Number::Count(1), format::Type::Integer, Type::Int32(len)) => {
77                get_i32_value(self.src, len, i)
78            }
79            (Number::Count(1), format::Type::Float, Type::Float(len)) => {
80                get_f32_value(self.src, len, i)
81            }
82            (Number::Count(1), format::Type::Character, Type::String(len)) => {
83                get_char_value(self.src, len, i)
84            }
85            (Number::Count(1), format::Type::String, Type::String(len)) => {
86                get_string_value(self.src, len, i)
87            }
88
89            (_, format::Type::Integer, Type::Int8(len)) => get_i8_array_value(self.src, len, i),
90            (_, format::Type::Integer, Type::Int16(len)) => get_i16_array_value(self.src, len, i),
91            (_, format::Type::Integer, Type::Int32(len)) => get_i32_array_value(self.src, len, i),
92            (_, format::Type::Float, Type::Float(len)) => get_f32_array_value(self.src, len, i),
93            (_, format::Type::Character, Type::String(len)) => {
94                get_char_array_value(self.src, len, i)
95            }
96            (_, format::Type::String, Type::String(len)) => {
97                get_string_array_value(self.src, len, i)
98            }
99
100            _ => todo!("unhandled type"),
101        };
102
103        match value {
104            Some(Some(value)) => Some(Some(Ok(value))),
105            Some(None) => Some(None),
106            None => None,
107        }
108    }
109}
110
111impl vcf::variant::record::samples::Series for Series<'_> {
112    fn name<'a, 'h: 'a>(&'a self, header: &'h vcf::Header) -> io::Result<&'a str> {
113        header
114            .string_maps()
115            .strings()
116            .get_index(self.id)
117            .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "invalid string map ID"))
118    }
119
120    fn get<'a, 'h: 'a>(
121        &'a self,
122        header: &'h vcf::Header,
123        i: usize,
124    ) -> Option<Option<io::Result<Value<'a>>>> {
125        self.get(header, i)
126    }
127
128    fn iter<'a, 'h: 'a>(
129        &'a self,
130        header: &'h vcf::Header,
131    ) -> Box<dyn Iterator<Item = io::Result<Option<Value<'a>>>> + 'a> {
132        Box::new((0..self.len()).map(|i| {
133            self.get(header, i)
134                .ok_or_else(|| io::Error::from(io::ErrorKind::InvalidData))?
135                .transpose()
136        }))
137    }
138}
139
140pub(super) fn read_series<'a>(src: &mut &'a [u8], sample_count: usize) -> io::Result<Series<'a>> {
141    fn size_of(ty: Type) -> usize {
142        match ty {
143            Type::Int8(n) => mem::size_of::<i8>() * n,
144            Type::Int16(n) => mem::size_of::<i16>() * n,
145            Type::Int32(n) => mem::size_of::<i32>() * n,
146            Type::Float(n) => mem::size_of::<f32>() * n,
147            Type::String(n) => mem::size_of::<u8>() * n,
148        }
149    }
150
151    let id = read_string_map_index(src)?;
152    let ty = read_type(src)?.expect("invalid type");
153
154    let len = size_of(ty) * sample_count;
155    let (buf, rest) = src.split_at(len);
156
157    *src = rest;
158
159    Ok(Series {
160        id,
161        ty,
162        sample_count,
163        src: buf,
164    })
165}
166
167fn read_string_map_index(src: &mut &[u8]) -> io::Result<usize> {
168    match read_value(src)?.and_then(|v| v.as_int()) {
169        Some(i) => usize::try_from(i).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)),
170        None => Err(io::Error::new(
171            io::ErrorKind::InvalidData,
172            "invalid string map index",
173        )),
174    }
175}
176
177fn range<N>(i: usize, len: usize) -> Range<usize> {
178    let size = mem::size_of::<N>();
179    let start = size * i * len;
180    let end = start + size * len;
181    start..end
182}
183
184fn get_i8_value(src: &[u8], len: usize, i: usize) -> Option<Option<Value<'_>>> {
185    use crate::record::codec::value::Int8;
186
187    let src = src.get(range::<i8>(i, len))?;
188
189    let value = match Int8::from(src[0] as i8) {
190        Int8::Value(n) => Some(Value::Integer(i32::from(n))),
191        Int8::Missing => None,
192        Int8::EndOfVector | Int8::Reserved(_) => todo!(),
193    };
194
195    Some(value)
196}
197
198fn get_i8_array_value(src: &[u8], len: usize, i: usize) -> Option<Option<Value<'_>>> {
199    let src = src.get(range::<i8>(i, len))?;
200    let values = Values::<'_, i8>::new(src);
201    Some(Some(Value::Array(Array::Integer(Box::new(values)))))
202}
203
204fn get_i16_value(src: &[u8], len: usize, i: usize) -> Option<Option<Value<'_>>> {
205    use crate::record::codec::value::Int16;
206
207    let src = src.get(range::<i16>(i, len))?;
208
209    // SAFETY: `src` is 2 bytes.
210    let value = match Int16::from(i16::from_le_bytes(src.try_into().unwrap())) {
211        Int16::Value(n) => Some(Value::Integer(i32::from(n))),
212        Int16::Missing => None,
213        Int16::EndOfVector | Int16::Reserved(_) => todo!(),
214    };
215
216    Some(value)
217}
218
219fn get_i16_array_value(src: &[u8], len: usize, i: usize) -> Option<Option<Value<'_>>> {
220    let src = src.get(range::<i16>(i, len))?;
221    let values = Values::<'_, i16>::new(src);
222    Some(Some(Value::Array(Array::Integer(Box::new(values)))))
223}
224
225fn get_i32_value(src: &[u8], len: usize, i: usize) -> Option<Option<Value<'_>>> {
226    use crate::record::codec::value::Int32;
227
228    let src = src.get(range::<i32>(i, len))?;
229
230    // SAFETY: `src` is 2 bytes.
231    let value = match Int32::from(i32::from_le_bytes(src.try_into().unwrap())) {
232        Int32::Value(n) => Some(Value::Integer(n)),
233        Int32::Missing => None,
234        Int32::EndOfVector | Int32::Reserved(_) => todo!(),
235    };
236
237    Some(value)
238}
239
240fn get_i32_array_value(src: &[u8], len: usize, i: usize) -> Option<Option<Value<'_>>> {
241    let src = src.get(range::<i32>(i, len))?;
242    let values = Values::<'_, i32>::new(src);
243    Some(Some(Value::Array(Array::Integer(Box::new(values)))))
244}
245
246fn get_f32_value(src: &[u8], len: usize, i: usize) -> Option<Option<Value<'_>>> {
247    use crate::record::codec::value::Float;
248
249    let src = src.get(range::<f32>(i, len))?;
250
251    // SAFETY: `src` is 2 bytes.
252    let value = match Float::from(f32::from_le_bytes(src.try_into().unwrap())) {
253        Float::Value(n) => Some(Value::Float(n)),
254        Float::Missing => None,
255        Float::EndOfVector | Float::Reserved(_) => todo!(),
256    };
257
258    Some(value)
259}
260
261fn get_f32_array_value(src: &[u8], len: usize, i: usize) -> Option<Option<Value<'_>>> {
262    let src = src.get(range::<f32>(i, len))?;
263    let values = Values::<'_, f32>::new(src);
264    Some(Some(Value::Array(Array::Float(Box::new(values)))))
265}
266
267fn get_string(src: &[u8], len: usize, i: usize) -> Option<&str> {
268    const NUL: u8 = 0x00;
269
270    let src = src.get(range::<u8>(i, len))?;
271
272    let src = match src.iter().position(|&b| b == NUL) {
273        Some(i) => &src[..i],
274        None => src,
275    };
276
277    Some(
278        str::from_utf8(src)
279            .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
280            .unwrap(), // TODO
281    )
282}
283
284fn get_char_value(src: &[u8], len: usize, i: usize) -> Option<Option<Value<'_>>> {
285    const MISSING: char = '.';
286
287    let s = get_string(src, len, i)?;
288
289    // TODO
290    let c = s.chars().next().unwrap();
291
292    match c {
293        MISSING => Some(None),
294        _ => Some(Some(Value::Character(c))),
295    }
296}
297
298fn get_char_array_value(src: &[u8], len: usize, i: usize) -> Option<Option<Value<'_>>> {
299    let s = get_string(src, len, i)?;
300    Some(Some(Value::Array(Array::Character(Box::new(s)))))
301}
302
303fn get_string_value(src: &[u8], len: usize, i: usize) -> Option<Option<Value<'_>>> {
304    const MISSING: &str = ".";
305
306    match get_string(src, len, i)? {
307        MISSING => Some(None),
308        s => Some(Some(Value::String(Cow::from(s)))),
309    }
310}
311
312fn get_string_array_value(src: &[u8], len: usize, i: usize) -> Option<Option<Value<'_>>> {
313    let s = get_string(src, len, i)?;
314    Some(Some(Value::Array(Array::String(Box::new(s)))))
315}
316
317fn get_genotype_value<'r>(
318    src: &'r [u8],
319    header: &vcf::Header,
320    len: usize,
321    i: usize,
322) -> Option<Option<io::Result<Value<'r>>>> {
323    use self::value::Genotype;
324
325    let src = src.get(range::<i8>(i, len))?;
326
327    Some(Some(Ok(Value::Genotype(Box::new(Genotype::new(
328        header.file_format(),
329        src,
330    ))))))
331}
332
333#[cfg(test)]
334mod tests {
335    use noodles_vcf::{
336        header::{record::value::Map, StringMaps},
337        variant::record::samples::Series as _,
338    };
339
340    use super::*;
341
342    const NAME: &str = "value";
343
344    fn build_header_with_format<I>(name: I, number: Number, ty: format::Type) -> vcf::Header
345    where
346        I: Into<String>,
347    {
348        let mut header = vcf::Header::builder()
349            .add_format(
350                name,
351                Map::builder()
352                    .set_number(number)
353                    .set_type(ty)
354                    .set_description("")
355                    .build()
356                    .unwrap(),
357            )
358            .build();
359
360        *header.string_maps_mut() = StringMaps::try_from(&header).unwrap();
361
362        header
363    }
364
365    #[test]
366    fn test_get_with_int8_values() {
367        fn t(series: &Series<'_>, header: &vcf::Header, i: usize, expected: Option<i32>) {
368            let actual = match series.get(header, i).unwrap().transpose().unwrap() {
369                Some(Value::Integer(n)) => Some(n),
370                Some(_) => panic!(),
371                None => None,
372            };
373
374            assert_eq!(actual, expected);
375        }
376
377        let header = build_header_with_format(NAME, Number::Count(1), format::Type::Integer);
378        let id = header.string_maps().strings().get_index_of(NAME).unwrap();
379        let src = &[
380            0x05, // Some(5)
381            0x08, // Some(5)
382            0x80, // None
383        ];
384
385        let series = Series {
386            id,
387            ty: Type::Int8(1),
388            sample_count: 3,
389            src,
390        };
391
392        t(&series, &header, 0, Some(5));
393        t(&series, &header, 1, Some(8));
394        t(&series, &header, 2, None);
395
396        assert!(series.get(&header, 3).is_none());
397    }
398
399    #[test]
400    fn test_get_with_int8_array_values() {
401        fn t(
402            series: &Series<'_>,
403            header: &vcf::Header,
404            i: usize,
405            expected: Option<&[Option<i32>]>,
406        ) {
407            let actual = match series.get(header, i).unwrap().transpose().unwrap() {
408                Some(Value::Array(Array::Integer(values))) => {
409                    Some(values.iter().collect::<Result<Vec<_>, _>>().unwrap())
410                }
411                Some(_) => panic!(),
412                None => None,
413            };
414
415            assert_eq!(actual.as_deref(), expected);
416        }
417
418        let header = build_header_with_format(NAME, Number::Count(2), format::Type::Integer);
419        let id = header.string_maps().strings().get_index_of(NAME).unwrap();
420        let src = &[
421            0x05, 0x08, // Some([Some(5), Some(8)])
422            0x0d, 0x80, // Some([Some(13), None])
423            0x15, 0x81, // Some([Some(21)])
424            0x80, 0x81, // None
425        ];
426
427        let series = Series {
428            id,
429            ty: Type::Int8(2),
430            sample_count: 4,
431            src,
432        };
433
434        t(&series, &header, 0, Some(&[Some(5), Some(8)]));
435        t(&series, &header, 1, Some(&[Some(13), None]));
436        t(&series, &header, 2, Some(&[Some(21)]));
437        t(&series, &header, 3, Some(&[None])); // FIXME
438
439        assert!(series.get(&header, 4).is_none());
440    }
441
442    #[test]
443    fn test_get_with_int16_values() {
444        fn t(series: &Series<'_>, header: &vcf::Header, i: usize, expected: Option<i32>) {
445            let actual = match series.get(header, i).unwrap().transpose().unwrap() {
446                Some(Value::Integer(n)) => Some(n),
447                Some(_) => panic!(),
448                None => None,
449            };
450
451            assert_eq!(actual, expected);
452        }
453
454        let header = build_header_with_format(NAME, Number::Count(1), format::Type::Integer);
455        let id = header.string_maps().strings().get_index_of(NAME).unwrap();
456        let src = &[
457            0x05, 0x00, // Some(5)
458            0x08, 0x00, // Some(8)
459            0x00, 0x80, // None
460        ];
461
462        let series = Series {
463            id,
464            ty: Type::Int16(1),
465            sample_count: 3,
466            src,
467        };
468
469        t(&series, &header, 0, Some(5));
470        t(&series, &header, 1, Some(8));
471        t(&series, &header, 2, None);
472
473        assert!(series.get(&header, 3).is_none());
474    }
475
476    #[test]
477    fn test_get_with_int16_array_values() {
478        fn t(
479            series: &Series<'_>,
480            header: &vcf::Header,
481            i: usize,
482            expected: Option<&[Option<i32>]>,
483        ) {
484            let actual = match series.get(header, i).unwrap().transpose().unwrap() {
485                Some(Value::Array(Array::Integer(values))) => {
486                    Some(values.iter().collect::<Result<Vec<_>, _>>().unwrap())
487                }
488                Some(_) => panic!(),
489                None => None,
490            };
491
492            assert_eq!(actual.as_deref(), expected);
493        }
494
495        let header = build_header_with_format(NAME, Number::Count(2), format::Type::Integer);
496        let id = header.string_maps().strings().get_index_of(NAME).unwrap();
497        let src = &[
498            0x05, 0x00, 0x08, 0x00, // Some([Some(5), Some(8)])
499            0x0d, 0x00, 0x00, 0x80, // Some([Some(13), None])
500            0x15, 0x00, 0x01, 0x80, // Some([Some(21)])
501            0x00, 0x80, 0x01, 0x80, // None
502        ];
503
504        let series = Series {
505            id,
506            ty: Type::Int16(2),
507            sample_count: 4,
508            src,
509        };
510
511        t(&series, &header, 0, Some(&[Some(5), Some(8)]));
512        t(&series, &header, 1, Some(&[Some(13), None]));
513        t(&series, &header, 2, Some(&[Some(21)]));
514        t(&series, &header, 3, Some(&[None])); // FIXME
515
516        assert!(series.get(&header, 4).is_none());
517    }
518
519    #[test]
520    fn test_get_with_int32_values() {
521        fn t(series: &Series<'_>, header: &vcf::Header, i: usize, expected: Option<i32>) {
522            let actual = match series.get(header, i).unwrap().transpose().unwrap() {
523                Some(Value::Integer(n)) => Some(n),
524                Some(_) => panic!(),
525                None => None,
526            };
527
528            assert_eq!(actual, expected);
529        }
530
531        let header = build_header_with_format(NAME, Number::Count(1), format::Type::Integer);
532        let id = header.string_maps().strings().get_index_of(NAME).unwrap();
533        let src = &[
534            0x05, 0x00, 0x00, 0x00, // Some(5)
535            0x08, 0x00, 0x00, 0x00, // Some(8)
536            0x00, 0x00, 0x00, 0x80, // None
537        ];
538
539        let series = Series {
540            id,
541            ty: Type::Int32(1),
542            sample_count: 3,
543            src,
544        };
545
546        t(&series, &header, 0, Some(5));
547        t(&series, &header, 1, Some(8));
548        t(&series, &header, 2, None);
549
550        assert!(series.get(&header, 3).is_none());
551    }
552
553    #[test]
554    fn test_get_with_int32_array_values() {
555        fn t(
556            series: &Series<'_>,
557            header: &vcf::Header,
558            i: usize,
559            expected: Option<&[Option<i32>]>,
560        ) {
561            let actual = match series.get(header, i).unwrap().transpose().unwrap() {
562                Some(Value::Array(Array::Integer(values))) => {
563                    Some(values.iter().collect::<Result<Vec<_>, _>>().unwrap())
564                }
565                Some(_) => panic!(),
566                None => None,
567            };
568
569            assert_eq!(actual.as_deref(), expected);
570        }
571
572        let header = build_header_with_format(NAME, Number::Count(2), format::Type::Integer);
573        let id = header.string_maps().strings().get_index_of(NAME).unwrap();
574        let src = &[
575            0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, // Some([Some(5), Some(8)])
576            0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // Some([Some(13), None])
577            0x15, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x80, // Some([Some(21)])
578            0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, // None
579        ];
580
581        let series = Series {
582            id,
583            ty: Type::Int32(2),
584            sample_count: 4,
585            src,
586        };
587
588        t(&series, &header, 0, Some(&[Some(5), Some(8)]));
589        t(&series, &header, 1, Some(&[Some(13), None]));
590        t(&series, &header, 2, Some(&[Some(21)]));
591        t(&series, &header, 3, Some(&[None])); // FIXME
592
593        assert!(series.get(&header, 4).is_none());
594    }
595
596    #[test]
597    fn test_get_with_float_values() {
598        fn t(series: &Series<'_>, header: &vcf::Header, i: usize, expected: Option<f32>) {
599            let actual = match series.get(header, i).unwrap().transpose().unwrap() {
600                Some(Value::Float(n)) => Some(n),
601                Some(_) => panic!(),
602                None => None,
603            };
604
605            assert_eq!(actual, expected);
606        }
607
608        let header = build_header_with_format(NAME, Number::Count(1), format::Type::Float);
609        let id = header.string_maps().strings().get_index_of(NAME).unwrap();
610        let src = &[
611            0x00, 0x00, 0x00, 0x00, // Some(0.0)
612            0x00, 0x00, 0x80, 0x3f, // Some(1.0)
613            0x01, 0x00, 0x80, 0x7f, // None
614        ];
615
616        let series = Series {
617            id,
618            ty: Type::Float(1),
619            sample_count: 3,
620            src,
621        };
622
623        t(&series, &header, 0, Some(0.0));
624        t(&series, &header, 1, Some(1.0));
625        t(&series, &header, 2, None);
626
627        assert!(series.get(&header, 3).is_none());
628    }
629
630    #[test]
631    fn test_get_with_float_array_values() {
632        fn t(
633            series: &Series<'_>,
634            header: &vcf::Header,
635            i: usize,
636            expected: Option<&[Option<f32>]>,
637        ) {
638            let actual = match series.get(header, i).unwrap().transpose().unwrap() {
639                Some(Value::Array(Array::Float(values))) => {
640                    Some(values.iter().collect::<Result<Vec<_>, _>>().unwrap())
641                }
642                Some(_) => panic!(),
643                None => None,
644            };
645
646            assert_eq!(actual.as_deref(), expected);
647        }
648
649        let header = build_header_with_format(NAME, Number::Count(2), format::Type::Float);
650        let id = header.string_maps().strings().get_index_of(NAME).unwrap();
651        let src = &[
652            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, // Some([Some(0.0), Some(1.0)])
653            0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x80, 0x7f, // Some([Some(0.0), None])
654            0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x80, 0x7f, // Some([Some(0.0)])
655            0x01, 0x00, 0x80, 0x7f, 0x02, 0x00, 0x80, 0x7f, // None
656        ];
657
658        let series = Series {
659            id,
660            ty: Type::Float(2),
661            sample_count: 4,
662            src,
663        };
664
665        t(&series, &header, 0, Some(&[Some(0.0), Some(1.0)]));
666        t(&series, &header, 1, Some(&[Some(0.0), None]));
667        t(&series, &header, 2, Some(&[Some(0.0)]));
668        t(&series, &header, 3, Some(&[None])); // FIXME
669
670        assert!(series.get(&header, 4).is_none());
671    }
672
673    #[test]
674    fn test_get_with_character_value() {
675        fn t(series: &Series<'_>, header: &vcf::Header, i: usize, expected: Option<char>) {
676            let actual = match series.get(header, i).unwrap().transpose().unwrap() {
677                Some(Value::Character(c)) => Some(c),
678                Some(_) => panic!(),
679                None => None,
680            };
681
682            assert_eq!(actual, expected);
683        }
684
685        let header = build_header_with_format(NAME, Number::Count(1), format::Type::Character);
686        let id = header.string_maps().strings().get_index_of(NAME).unwrap();
687
688        #[allow(clippy::byte_char_slices)]
689        let src = &[
690            b'n', // Some('n')
691            b'd', // Some('d')
692            b'.', // None
693        ];
694
695        let series = Series {
696            id,
697            ty: Type::String(1),
698            sample_count: 3,
699            src,
700        };
701
702        t(&series, &header, 0, Some('n'));
703        t(&series, &header, 1, Some('d'));
704        t(&series, &header, 2, None);
705
706        assert!(series.get(&header, 3).is_none());
707    }
708
709    #[test]
710    fn test_get_with_character_array_value() {
711        fn t(series: &Series<'_>, header: &vcf::Header, i: usize, expected: &[Option<char>]) {
712            match series.get(header, i).unwrap().unwrap().unwrap() {
713                Value::Array(Array::Character(values)) => {
714                    assert_eq!(
715                        values.iter().collect::<Result<Vec<_>, _>>().unwrap(),
716                        expected,
717                    );
718                }
719                _ => panic!(),
720            }
721        }
722
723        let header = build_header_with_format(NAME, Number::Count(2), format::Type::Character);
724        let id = header.string_maps().strings().get_index_of(NAME).unwrap();
725        let src = &[
726            b'n', b',', b'd', // [Some('n'), Some('d')]
727            b'n', b',', b'.', // [Some('n'), None]
728            b'n', 0x00, 0x00, // [Some('n')]
729        ];
730
731        let series = Series {
732            id,
733            ty: Type::String(3),
734            sample_count: 3,
735            src,
736        };
737
738        t(&series, &header, 0, &[Some('n'), Some('d')]);
739        t(&series, &header, 1, &[Some('n'), None]);
740        t(&series, &header, 2, &[Some('n')]);
741
742        assert!(series.get(&header, 3).is_none());
743    }
744
745    #[test]
746    fn test_get_with_string_value() {
747        fn t(series: &Series<'_>, header: &vcf::Header, i: usize, expected: Option<&str>) {
748            let actual = match series.get(header, i).unwrap().transpose().unwrap() {
749                Some(Value::String(s)) => Some(s),
750                Some(_) => panic!(),
751                None => None,
752            };
753
754            assert_eq!(actual, expected.map(Cow::from));
755        }
756
757        let header = build_header_with_format(NAME, Number::Count(1), format::Type::String);
758        let id = header.string_maps().strings().get_index_of(NAME).unwrap();
759        let src = &[
760            b'n', 0x00, 0x00, 0x00, // Some("n")
761            b'n', b'd', b'l', b's', // Some("ndls")
762            b'.', 0x00, 0x00, 0x00, // None
763        ];
764
765        let series = Series {
766            id,
767            ty: Type::String(4),
768            sample_count: 3,
769            src,
770        };
771
772        t(&series, &header, 0, Some("n"));
773        t(&series, &header, 1, Some("ndls"));
774        t(&series, &header, 2, None);
775
776        assert!(series.get(&header, 3).is_none());
777    }
778
779    #[test]
780    fn test_get_with_string_array_value() -> Result<(), Box<dyn std::error::Error>> {
781        fn t(series: &Series<'_>, header: &vcf::Header, i: usize, expected: &[Option<&str>]) {
782            let expected: Vec<_> = expected.iter().map(|value| value.map(Cow::from)).collect();
783
784            match series.get(header, i).unwrap().unwrap().unwrap() {
785                Value::Array(Array::String(values)) => {
786                    assert_eq!(
787                        values.iter().collect::<Result<Vec<_>, _>>().unwrap(),
788                        expected,
789                    );
790                }
791                _ => panic!(),
792            }
793        }
794
795        let header = build_header_with_format(NAME, Number::Count(2), format::Type::String);
796        let id = header.string_maps().strings().get_index_of(NAME).unwrap();
797        let src = &[
798            b'n', 0x00, 0x00, 0x00, // "n"
799            b'n', b',', b'l', 0x00, // "n,l"
800            b'n', b',', b'l', b's', // "n,ls"
801        ];
802
803        let series = Series {
804            id,
805            ty: Type::String(4),
806            sample_count: 3,
807            src,
808        };
809
810        t(&series, &header, 0, &[Some("n")]);
811        t(&series, &header, 1, &[Some("n"), Some("l")]);
812        t(&series, &header, 2, &[Some("n"), Some("ls")]);
813
814        assert!(series.get(&header, 3).is_none());
815
816        Ok(())
817    }
818
819    #[test]
820    fn test_iter() {
821        let header = build_header_with_format(NAME, Number::Count(1), format::Type::Integer);
822        let id = header.string_maps().strings().get_index_of(NAME).unwrap();
823        let src = &[
824            0x05, // Some(5)
825            0x08, // Some(5)
826            0x80, // None
827        ];
828
829        let series = Series {
830            id,
831            ty: Type::Int8(1),
832            sample_count: 3,
833            src,
834        };
835
836        let actual: Vec<_> = series
837            .iter(&header)
838            .map(|result| match result {
839                Ok(Some(Value::Integer(n))) => Some(n),
840                Ok(None) => None,
841                _ => panic!(),
842            })
843            .collect();
844
845        assert_eq!(actual, [Some(5), Some(8), None]);
846    }
847}