1pub 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
18pub struct Series<'r> {
20 id: usize,
21 ty: Type,
22 sample_count: usize,
23 src: &'r [u8],
24}
25
26impl<'r> Series<'r> {
27 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 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 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 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 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(), )
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 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, 0x08, 0x80, ];
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, 0x0d, 0x80, 0x15, 0x81, 0x80, 0x81, ];
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])); 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, 0x08, 0x00, 0x00, 0x80, ];
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, 0x0d, 0x00, 0x00, 0x80, 0x15, 0x00, 0x01, 0x80, 0x00, 0x80, 0x01, 0x80, ];
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])); 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, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, ];
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, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x15, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, ];
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])); 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, 0x00, 0x00, 0x80, 0x3f, 0x01, 0x00, 0x80, 0x7f, ];
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, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x80, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x80, 0x7f, 0x01, 0x00, 0x80, 0x7f, 0x02, 0x00, 0x80, 0x7f, ];
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])); 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', b'd', b'.', ];
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', b'n', b',', b'.', b'n', 0x00, 0x00, ];
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, b'n', b'd', b'l', b's', b'.', 0x00, 0x00, 0x00, ];
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, b'n', b',', b'l', 0x00, b'n', b',', b'l', b's', ];
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, 0x08, 0x80, ];
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}