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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
//! CRAM record tag and fields.

use std::{convert::TryFrom, error, fmt, str};

mod key;

pub use self::key::Key;

use noodles_bam::record::data::field::Value;
use noodles_sam as sam;

/// A CRAM record tag.
#[derive(Clone, Debug, PartialEq)]
pub struct Tag {
    key: Key,
    value: Value,
}

impl Tag {
    /// Creates a CRAM record tag.
    ///
    /// # Examples
    ///
    /// ```
    /// use noodles_bam::record::data::field::Value;
    /// use noodles_cram::record::{tag::Key, Tag};
    ///
    /// let value = Value::Int8(1);
    /// let key = Key::new([b'N', b'H'], value.ty());
    ///
    /// let tag = Tag::new(key, value);
    /// ```
    pub fn new(key: Key, value: Value) -> Self {
        Self { key, value }
    }

    /// Returns the tag key.
    ///
    /// # Examples
    ///
    /// ```
    /// use noodles_bam::record::data::field::Value;
    /// use noodles_cram::record::{tag::Key, Tag};
    ///
    /// let value = Value::Int8(1);
    /// let key = Key::new([b'N', b'H'], value.ty());
    ///
    /// let tag = Tag::new(key.clone(), value);
    /// assert_eq!(tag.key(), key);
    /// ```
    pub fn key(&self) -> Key {
        self.key
    }

    /// Returns the tag value.
    ///
    /// # Examples
    ///
    /// ```
    /// use noodles_bam::record::data::field::Value;
    /// use noodles_cram::record::{tag::Key, Tag};
    ///
    /// let value = Value::Int8(1);
    /// let key = Key::new([b'N', b'H'], value.ty());
    ///
    /// let tag = Tag::new(key, value.clone());
    /// assert_eq!(tag.value(), &value);
    /// ```
    pub fn value(&self) -> &Value {
        &self.value
    }
}

/// An error returned when a CRAM record tag fails to convert to a SAM record data field.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum TryFromTagError {
    /// The tag is invalid.
    InvalidTag,
}

impl error::Error for TryFromTagError {}

impl fmt::Display for TryFromTagError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::InvalidTag => f.write_str("invalid tag"),
        }
    }
}

impl TryFrom<Tag> for sam::record::data::Field {
    type Error = TryFromTagError;

    fn try_from(tag: Tag) -> Result<Self, Self::Error> {
        use sam::record::data::Field;

        let raw_tag = tag.key().tag();
        let sam_tag = str::from_utf8(&raw_tag)
            .map_err(|_| TryFromTagError::InvalidTag)
            .and_then(|s| s.parse().map_err(|_| TryFromTagError::InvalidTag))?;

        let sam_value = tag.value.into();

        Ok(Field::new(sam_tag, sam_value))
    }
}

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

    #[test]
    fn test_try_from_tag_for_sam_record_data_field() {
        use sam::record::data::Field;

        let value = Value::Int8(1);
        let key = Key::new([b'N', b'H'], value.ty());
        let tag = Tag::new(key, value);
        let actual = Field::try_from(tag);

        let expected = Ok(Field::new(
            sam::record::data::field::Tag::AlignmentHitCount,
            sam::record::data::field::Value::Int(1),
        ));

        assert_eq!(actual, expected);
    }
}