noodles_sam/alignment/record_buf/data/field/
value.rs

1//! Alignment record data field value buffer.
2
3mod array;
4
5use bstr::BString;
6
7pub use self::array::Array;
8use crate::alignment::record::data::field::Type;
9
10/// An alignment record data field value buffer.
11#[derive(Clone, Debug, PartialEq)]
12pub enum Value {
13    /// A character (`A`).
14    Character(u8),
15    /// An 8-bit integer (`c`).
16    Int8(i8),
17    /// An 8-bit unsigned integer (`C`).
18    UInt8(u8),
19    /// A 16-bit integer (`s`).
20    Int16(i16),
21    /// A 16-bit unsigned integer (`S`).
22    UInt16(u16),
23    /// A 32-bit integer (`i`).
24    Int32(i32),
25    /// A 32-bit unsigned integer (`I`).
26    UInt32(u32),
27    /// A single-precision floating-point (`f`).
28    Float(f32),
29    /// A string (`Z`).
30    String(BString),
31    /// A hex string (`H`).
32    Hex(BString),
33    /// An array (`B`).
34    Array(Array),
35}
36
37impl Value {
38    /// Returns the type of the value.
39    ///
40    /// # Examples
41    ///
42    /// ```
43    /// use noodles_sam::alignment::{record::data::field::Type, record_buf::data::field::Value};
44    /// assert_eq!(Value::Int32(0).ty(), Type::Int32);
45    /// ```
46    pub fn ty(&self) -> Type {
47        match self {
48            Self::Character(_) => Type::Character,
49            Self::Int8(_) => Type::Int8,
50            Self::UInt8(_) => Type::UInt8,
51            Self::Int16(_) => Type::Int16,
52            Self::UInt16(_) => Type::UInt16,
53            Self::Int32(_) => Type::Int32,
54            Self::UInt32(_) => Type::UInt32,
55            Self::Float(_) => Type::Float,
56            Self::String(_) => Type::String,
57            Self::Hex(_) => Type::Hex,
58            Self::Array(_) => Type::Array,
59        }
60    }
61
62    /// Returns the value as a 64-bit integer.
63    ///
64    /// This is a convenience method that converts any integer to an `i64`, which captures the
65    /// entire range of all alignment record data field integer values.
66    ///
67    /// # Examples
68    ///
69    /// ```
70    /// use noodles_sam::alignment::record_buf::data::field::Value;
71    /// assert_eq!(Value::from(0).as_int(), Some(0));
72    /// assert_eq!(Value::from("noodles").as_int(), None);
73    /// ```
74    pub fn as_int(&self) -> Option<i64> {
75        match self {
76            Self::Int8(n) => Some(i64::from(*n)),
77            Self::UInt8(n) => Some(i64::from(*n)),
78            Self::Int16(n) => Some(i64::from(*n)),
79            Self::UInt16(n) => Some(i64::from(*n)),
80            Self::Int32(n) => Some(i64::from(*n)),
81            Self::UInt32(n) => Some(i64::from(*n)),
82            _ => None,
83        }
84    }
85
86    /// Returns whether the value is an integer.
87    ///
88    /// # Examples
89    ///
90    /// ```
91    /// use noodles_sam::alignment::record_buf::data::field::Value;
92    /// assert!(Value::Int32(0).is_int());
93    /// assert!(!Value::Float(0.0).is_int());
94    /// ```
95    pub fn is_int(&self) -> bool {
96        matches!(
97            self,
98            Self::Int8(_)
99                | Self::UInt8(_)
100                | Self::Int16(_)
101                | Self::UInt16(_)
102                | Self::Int32(_)
103                | Self::UInt32(_)
104        )
105    }
106}
107
108impl From<i8> for Value {
109    fn from(n: i8) -> Self {
110        if n >= 0 {
111            Self::from(n as u8)
112        } else {
113            Self::Int8(n)
114        }
115    }
116}
117
118impl From<u8> for Value {
119    fn from(n: u8) -> Self {
120        Self::UInt8(n)
121    }
122}
123
124impl From<i16> for Value {
125    fn from(n: i16) -> Self {
126        if n >= 0 {
127            Self::from(n as u16)
128        } else if n >= i16::from(i8::MIN) {
129            Self::Int8(n as i8)
130        } else {
131            Self::Int16(n)
132        }
133    }
134}
135
136impl From<u16> for Value {
137    fn from(n: u16) -> Self {
138        if n <= u16::from(u8::MAX) {
139            Self::UInt8(n as u8)
140        } else {
141            Self::UInt16(n)
142        }
143    }
144}
145
146impl From<i32> for Value {
147    fn from(n: i32) -> Self {
148        if n >= 0 {
149            Self::from(n as u32)
150        } else if n >= i32::from(i8::MIN) {
151            Self::Int8(n as i8)
152        } else if n >= i32::from(i16::MIN) {
153            Self::Int16(n as i16)
154        } else {
155            Self::Int32(n)
156        }
157    }
158}
159
160impl From<u32> for Value {
161    fn from(n: u32) -> Self {
162        if n <= u32::from(u8::MAX) {
163            Self::UInt8(n as u8)
164        } else if n <= u32::from(u16::MAX) {
165            Self::UInt16(n as u16)
166        } else {
167            Self::UInt32(n)
168        }
169    }
170}
171
172impl From<f32> for Value {
173    fn from(n: f32) -> Self {
174        Value::Float(n)
175    }
176}
177
178impl From<&str> for Value {
179    fn from(s: &str) -> Self {
180        Self::String(s.into())
181    }
182}
183
184impl From<String> for Value {
185    fn from(s: String) -> Self {
186        Self::String(s.into())
187    }
188}
189
190impl From<Vec<i8>> for Value {
191    fn from(values: Vec<i8>) -> Self {
192        Value::Array(Array::Int8(values))
193    }
194}
195
196impl From<Vec<u8>> for Value {
197    fn from(values: Vec<u8>) -> Self {
198        Value::Array(Array::UInt8(values))
199    }
200}
201
202impl From<Vec<i16>> for Value {
203    fn from(values: Vec<i16>) -> Self {
204        Value::Array(Array::Int16(values))
205    }
206}
207
208impl From<Vec<u16>> for Value {
209    fn from(values: Vec<u16>) -> Self {
210        Value::Array(Array::UInt16(values))
211    }
212}
213
214impl From<Vec<i32>> for Value {
215    fn from(values: Vec<i32>) -> Self {
216        Value::Array(Array::Int32(values))
217    }
218}
219
220impl From<Vec<u32>> for Value {
221    fn from(values: Vec<u32>) -> Self {
222        Value::Array(Array::UInt32(values))
223    }
224}
225
226impl From<Vec<f32>> for Value {
227    fn from(values: Vec<f32>) -> Self {
228        Value::Array(Array::Float(values))
229    }
230}
231
232impl TryFrom<i64> for Value {
233    type Error = crate::io::reader::record_buf::data::field::value::ParseError;
234
235    fn try_from(n: i64) -> Result<Self, Self::Error> {
236        const MIN: i64 = i32::MIN as i64;
237        const MAX: i64 = u32::MAX as i64;
238
239        if n > MAX {
240            Err(Self::Error::InvalidIntegerValue)
241        } else if n >= 0 {
242            Ok(Self::from(n as u32))
243        } else if n >= i64::from(i8::MIN) {
244            Ok(Self::Int8(n as i8))
245        } else if n >= i64::from(i16::MIN) {
246            Ok(Self::Int16(n as i16))
247        } else if n >= MIN {
248            Ok(Self::Int32(n as i32))
249        } else {
250            Err(Self::Error::InvalidIntegerValue)
251        }
252    }
253}
254
255impl<'a> From<&'a Value> for crate::alignment::record::data::field::Value<'a> {
256    fn from(value_buf: &'a Value) -> Self {
257        match value_buf {
258            Value::Character(c) => Self::Character(*c),
259            Value::Int8(n) => Self::Int8(*n),
260            Value::UInt8(n) => Self::UInt8(*n),
261            Value::Int16(n) => Self::Int16(*n),
262            Value::UInt16(n) => Self::UInt16(*n),
263            Value::Int32(n) => Self::Int32(*n),
264            Value::UInt32(n) => Self::UInt32(*n),
265            Value::Float(n) => Self::Float(*n),
266            Value::String(s) => Self::String(s.as_ref()),
267            Value::Hex(s) => Self::Hex(s.as_ref()),
268            Value::Array(array) => Self::Array(array.into()),
269        }
270    }
271}
272
273#[cfg(test)]
274mod tests {
275    use super::*;
276
277    #[test]
278    fn test_ty() {
279        assert_eq!(Value::Character(b'n').ty(), Type::Character);
280        assert_eq!(Value::Int32(0).ty(), Type::Int32);
281        assert_eq!(Value::Float(0.0).ty(), Type::Float);
282        assert_eq!(Value::from("noodles").ty(), Type::String);
283        assert_eq!(Value::Hex(b"CAFE".into()).ty(), Type::Hex);
284        assert_eq!(Value::Array(Array::UInt8(vec![0])).ty(), Type::Array);
285    }
286
287    #[test]
288    fn test_from_i8_for_value() {
289        assert_eq!(Value::from(i8::MIN), Value::Int8(i8::MIN));
290        assert_eq!(Value::from(-1i8), Value::Int8(-1));
291
292        assert_eq!(Value::from(0i8), Value::UInt8(0));
293        assert_eq!(Value::from(1i8), Value::UInt8(1));
294        assert_eq!(Value::from(i8::MAX), Value::UInt8(i8::MAX as u8));
295    }
296
297    #[test]
298    fn test_from_u8_for_value() {
299        assert_eq!(Value::from(u8::MIN), Value::UInt8(u8::MIN));
300        assert_eq!(Value::from(u8::MAX), Value::UInt8(u8::MAX));
301    }
302
303    #[test]
304    fn test_from_i16_for_value() {
305        assert_eq!(Value::from(i16::MIN), Value::Int16(i16::MIN));
306        assert_eq!(Value::from(-129i16), Value::Int16(-129)); // i8::MIN - 1
307
308        assert_eq!(Value::from(-128i16), Value::Int8(-128)); // i8::MAX
309        assert_eq!(Value::from(-1i16), Value::Int8(-1));
310
311        assert_eq!(Value::from(0i16), Value::UInt8(0));
312        assert_eq!(Value::from(1i16), Value::UInt8(1));
313        assert_eq!(Value::from(255i16), Value::UInt8(255)); // u8::MAX
314
315        assert_eq!(Value::from(256i16), Value::UInt16(256)); // u8::MAX + 1
316        assert_eq!(Value::from(i16::MAX), Value::UInt16(i16::MAX as u16));
317    }
318
319    #[test]
320    fn test_from_u16_for_value() {
321        assert_eq!(Value::from(u16::MIN), Value::UInt8(0));
322        assert_eq!(Value::from(255u16), Value::UInt8(255));
323
324        assert_eq!(Value::from(256u16), Value::UInt16(256));
325        assert_eq!(Value::from(u16::MAX), Value::UInt16(u16::MAX));
326    }
327
328    #[test]
329    fn test_from_i32_for_value() {
330        assert_eq!(Value::from(i32::MIN), Value::Int32(i32::MIN));
331        assert_eq!(Value::from(-32769i32), Value::Int32(-32769)); // i16::MIN - 1
332
333        assert_eq!(Value::from(-32768i32), Value::Int16(-32768)); // i16::MIN
334        assert_eq!(Value::from(-129i32), Value::Int16(-129)); // i8::MIN - 1
335
336        assert_eq!(Value::from(-128i32), Value::Int8(-128)); // i8::MIN
337        assert_eq!(Value::from(-1i32), Value::Int8(-1));
338
339        assert_eq!(Value::from(0i32), Value::UInt8(0));
340        assert_eq!(Value::from(1i32), Value::UInt8(1));
341        assert_eq!(Value::from(255i32), Value::UInt8(255)); // u8::MAX
342
343        assert_eq!(Value::from(256i32), Value::UInt16(256)); // u8::MAX + 1
344        assert_eq!(Value::from(65535i32), Value::UInt16(65535)); // u16::MAX
345
346        assert_eq!(Value::from(65536i32), Value::UInt32(65536)); // u16::MAX + 1
347        assert_eq!(Value::from(i32::MAX), Value::UInt32(i32::MAX as u32));
348    }
349
350    #[test]
351    fn test_from_u32_for_value() {
352        assert_eq!(Value::from(u32::MIN), Value::UInt8(0));
353        assert_eq!(Value::from(255u32), Value::UInt8(255)); // u8::MAX
354
355        assert_eq!(Value::from(256u32), Value::UInt16(256)); // u8::MAX + 1
356        assert_eq!(Value::from(65535u32), Value::UInt16(65535)); // u16::MAX
357
358        assert_eq!(Value::from(65536u32), Value::UInt32(65536)); // u16::MAX + 1
359        assert_eq!(Value::from(u32::MAX), Value::UInt32(u32::MAX));
360    }
361
362    #[test]
363    fn test_from_f32_for_value() {
364        assert_eq!(Value::from(0.0f32), Value::Float(0.0));
365    }
366
367    #[test]
368    fn test_from_vec_i8_for_value() {
369        assert_eq!(Value::from(vec![0i8]), Value::Array(Array::Int8(vec![0])));
370    }
371
372    #[test]
373    fn test_from_vec_u8_for_value() {
374        assert_eq!(Value::from(vec![0u8]), Value::Array(Array::UInt8(vec![0])));
375    }
376
377    #[test]
378    fn test_from_vec_i16_for_value() {
379        assert_eq!(Value::from(vec![0i16]), Value::Array(Array::Int16(vec![0])));
380    }
381
382    #[test]
383    fn test_from_vec_u16_for_value() {
384        assert_eq!(
385            Value::from(vec![0u16]),
386            Value::Array(Array::UInt16(vec![0]))
387        );
388    }
389
390    #[test]
391    fn test_from_vec_i32_for_value() {
392        assert_eq!(Value::from(vec![0i32]), Value::Array(Array::Int32(vec![0])));
393    }
394
395    #[test]
396    fn test_from_vec_u32_for_value() {
397        assert_eq!(
398            Value::from(vec![0u32]),
399            Value::Array(Array::UInt32(vec![0]))
400        );
401    }
402
403    #[test]
404    fn test_from_vec_f32_for_value() {
405        assert_eq!(
406            Value::from(vec![0.0f32]),
407            Value::Array(Array::Float(vec![0.0]))
408        );
409    }
410
411    #[test]
412    fn test_try_from_i64_for_value(
413    ) -> Result<(), crate::io::reader::record_buf::data::field::value::ParseError> {
414        use crate::io::reader::record_buf::data::field::value::ParseError;
415
416        fn t(n: i64, expected: Value) -> Result<(), ParseError> {
417            let actual = Value::try_from(n)?;
418            assert_eq!(actual, expected);
419            Ok(())
420        }
421
422        assert_eq!(
423            Value::try_from(-2147483649i64),
424            Err(ParseError::InvalidIntegerValue)
425        );
426
427        t(-2147483648, Value::Int32(i32::MIN))?;
428        t(-2147483647, Value::Int32(-2147483647))?;
429
430        t(-32769, Value::Int32(-32769))?;
431        t(-32768, Value::Int16(i16::MIN))?;
432        t(-32767, Value::Int16(-32767))?;
433
434        t(-129, Value::Int16(-129))?;
435        t(-128, Value::Int8(i8::MIN))?;
436        t(-127, Value::Int8(-127))?;
437
438        t(-1, Value::Int8(-1))?;
439        t(0, Value::UInt8(0))?;
440        t(1, Value::UInt8(1))?;
441
442        t(254, Value::UInt8(254))?;
443        t(255, Value::UInt8(u8::MAX))?;
444        t(256, Value::UInt16(256))?;
445
446        t(65534, Value::UInt16(65534))?;
447        t(65535, Value::UInt16(u16::MAX))?;
448        t(65536, Value::UInt32(65536))?;
449
450        t(4294967294, Value::UInt32(4294967294))?;
451        t(4294967295, Value::UInt32(u32::MAX))?;
452
453        assert_eq!(
454            Value::try_from(4294967296i64),
455            Err(ParseError::InvalidIntegerValue)
456        );
457
458        Ok(())
459    }
460}