Skip to content

Commit

Permalink
vcf/variant/record/info/field/value/array/values: Percent-decode stri…
Browse files Browse the repository at this point in the history
…ng values

This changes the values iterator item from `&str` to `Cow<'_, str>`.

See #300.
  • Loading branch information
zaeleus committed Sep 6, 2024
1 parent fb8673a commit 410b94e
Show file tree
Hide file tree
Showing 7 changed files with 48 additions and 24 deletions.
5 changes: 3 additions & 2 deletions noodles-bcf/src/record/codec/encoder/site/info/field/value.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::{
borrow::Cow,
cmp,
io::{self, Write},
};
Expand Down Expand Up @@ -273,7 +274,7 @@ where

fn write_string_array_value<W>(
writer: &mut W,
values: Box<dyn Values<'_, &str> + '_>,
values: Box<dyn Values<'_, Cow<'_, str>> + '_>,
) -> io::Result<()>
where
W: Write,
Expand All @@ -286,7 +287,7 @@ where
}

if let Some(t) = result? {
s.push_str(t);
s.push_str(&t);
} else {
s.push(MISSING_VALUE);
}
Expand Down
5 changes: 5 additions & 0 deletions noodles-bcf/src/record/info/field/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,8 @@ fn type_mismatch_error(value: Option<TypedValue>, expected: Type) -> io::Error {

#[cfg(test)]
mod tests {
use std::borrow::Cow;

use super::*;

#[test]
Expand Down Expand Up @@ -392,6 +394,9 @@ mod tests {
fn t(mut src: &[u8], expected: &[Option<&str>]) {
match read_value(&mut src, Number::Count(2), Type::String) {
Ok(Some(Value::Array(Array::String(values)))) => {
let expected: Vec<_> =
expected.iter().map(|value| value.map(Cow::from)).collect();

assert!(matches!(
values.iter().collect::<io::Result<Vec<_>>>(),
Ok(vs) if vs == expected
Expand Down
9 changes: 9 additions & 0 deletions noodles-vcf/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@

## Unreleased

### Changed

* vcf/variant/record/info/field/value/array/values: Percent-decode string
values ([#300]).

This changes the values iterator item from `&str` to `Cow<'_, str>`.

[#300]: https://github.com/zaeleus/noodles/issues/300

### Fixed

* vcf/variant/record/info/field/value/array/values: Fix counting number of
Expand Down
2 changes: 1 addition & 1 deletion noodles-vcf/src/io/reader/record_buf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ mod quality_score;
mod reference_bases;
mod reference_sequence_name;
mod samples;
mod value;
pub(crate) mod value;

use std::{error, fmt};

Expand Down
4 changes: 2 additions & 2 deletions noodles-vcf/src/variant/record/info/field/value/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

mod values;

use std::{fmt, io};
use std::{borrow::Cow, fmt, io};

pub use self::values::Values;

Expand All @@ -15,7 +15,7 @@ pub enum Array<'a> {
/// A character array.
Character(Box<dyn Values<'a, char> + 'a>),
/// A string array.
String(Box<dyn Values<'a, &'a str> + 'a>),
String(Box<dyn Values<'a, Cow<'a, str>> + 'a>),
}

impl<'a> fmt::Debug for Array<'a> {
Expand Down
39 changes: 24 additions & 15 deletions noodles-vcf/src/variant/record/info/field/value/array/values.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use std::io;
use std::{borrow::Cow, io};

use crate::io::reader::record_buf::value::percent_decode;

/// Variant record info field array value values.
#[allow(clippy::len_without_is_empty)]
Expand Down Expand Up @@ -69,20 +71,20 @@ impl<'a> Values<'a, char> for &'a str {
}
}

impl<'a> Values<'a, &'a str> for &'a str {
impl<'a> Values<'a, Cow<'a, str>> for &'a str {
fn len(&self) -> usize {
count(self)
}

fn iter(&self) -> Box<dyn Iterator<Item = io::Result<Option<&'a str>>> + '_> {
Box::new(
self.split(DELIMITER)
.map(|s| match s {
MISSING => None,
_ => Some(s),
})
.map(Ok),
)
fn iter(&self) -> Box<dyn Iterator<Item = io::Result<Option<Cow<'a, str>>>> + '_> {
Box::new(self.split(DELIMITER).map(|s| {
match s {
MISSING => Ok(None),
_ => percent_decode(s)
.map(Some)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)),
}
}))
}
}

Expand All @@ -102,13 +104,20 @@ mod tests {
use super::*;

#[test]
fn test_string_values() {
fn test_string_values() -> io::Result<()> {
let src = "";
let values: Box<dyn Values<'_, &'_ str>> = Box::new(src);
let values: Box<dyn Values<'_, Cow<'_, str>>> = Box::new(src);
assert_eq!(values.len(), 0);

let src = "a,b";
let values: Box<dyn Values<'_, &'_ str>> = Box::new(src);
let src = "a,b%3Bc";
let values: Box<dyn Values<'_, Cow<'_, str>>> = Box::new(src);
assert_eq!(values.len(), 2);

let mut iter = values.iter();
assert_eq!(iter.next().transpose()?, Some(Some(Cow::from("a"))));
assert_eq!(iter.next().transpose()?, Some(Some(Cow::from("b;c"))));
assert!(iter.next().transpose()?.is_none());

Ok(())
}
}
8 changes: 4 additions & 4 deletions noodles-vcf/src/variant/record_buf/info/field/value/array.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::io;
use std::{borrow::Cow, io};

/// A variant record info field array value.
#[derive(Clone, Debug, PartialEq)]
Expand Down Expand Up @@ -45,14 +45,14 @@ where
}
}

impl<'a> crate::variant::record::info::field::value::array::Values<'a, &'a str>
impl<'a> crate::variant::record::info::field::value::array::Values<'a, Cow<'a, str>>
for Values<'a, String>
{
fn len(&self) -> usize {
self.0.len()
}

fn iter(&self) -> Box<dyn Iterator<Item = io::Result<Option<&'a str>>> + '_> {
Box::new(self.0.iter().map(|s| Ok(s.as_deref())))
fn iter(&self) -> Box<dyn Iterator<Item = io::Result<Option<Cow<'a, str>>>> + '_> {
Box::new(self.0.iter().map(|s| Ok(s.as_deref().map(Cow::from))))
}
}

0 comments on commit 410b94e

Please sign in to comment.