1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
//! SAM header record value.

use std::{error, fmt};

use indexmap::IndexMap;

/// An ordered map of raw tag-value pairs.
pub type Fields = IndexMap<String, String>;

/// A SAM header record value.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Value {
    /// A string.
    String(String),
    /// A map of fields.
    Map(Fields),
}

/// An error returned when raw SAM header record fields fail to convert.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum TryFromIteratorError {
    /// The input is empty.
    Empty,
    /// A tag is duplicated.
    DuplicateTag(String),
}

impl error::Error for TryFromIteratorError {}

impl fmt::Display for TryFromIteratorError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Empty => f.write_str("empty input"),
            Self::DuplicateTag(tag) => write!(f, "duplicate tag: {}", tag),
        }
    }
}

impl Value {
    /// Performs a conversion from a `(String, String)` iterator to a `Value::Map`.
    ///
    /// # Examples
    ///
    /// ```
    /// use noodles_sam::header::record::{value, Value};
    ///
    /// let actual = Value::try_from_iter(vec![("SN", "sq0"), ("LN", "8")])?;
    /// let expected = Value::Map(vec![
    ///     (String::from("SN"), String::from("sq0")),
    ///     (String::from("LN"), String::from("8")),
    /// ].into_iter().collect());
    ///
    /// assert_eq!(actual, expected);
    /// # Ok::<(), value::TryFromIteratorError>(())
    /// ```
    pub fn try_from_iter<I, K, V>(iter: I) -> Result<Self, TryFromIteratorError>
    where
        I: IntoIterator<Item = (K, V)>,
        K: Into<String>,
        V: Into<String>,
    {
        let mut fields = Fields::new();

        for (k, v) in iter {
            let key = k.into();
            let value = v.into();

            if fields.insert(key.clone(), value).is_some() {
                return Err(TryFromIteratorError::DuplicateTag(key));
            }
        }

        if fields.is_empty() {
            return Err(TryFromIteratorError::Empty);
        }

        Ok(Value::Map(fields))
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_try_from_iter() {
        assert_eq!(
            Value::try_from_iter(vec![(String::from("VN"), String::from("1.6")),]),
            Ok(Value::Map(
                vec![(String::from("VN"), String::from("1.6")),]
                    .into_iter()
                    .collect()
            ))
        );

        assert_eq!(
            Value::try_from_iter(vec![
                (String::from("VN"), String::from("1.6")),
                (String::from("VN"), String::from("1.6")),
            ]),
            Err(TryFromIteratorError::DuplicateTag(String::from("VN")))
        );

        assert_eq!(
            Value::try_from_iter(Vec::<(String, String)>::new()),
            Err(TryFromIteratorError::Empty)
        );
    }
}