use bytemuck::{Pod, Zeroable};
pub const RECORD_SIZE: usize = std::mem::size_of::<Record>();
#[derive(Copy, Clone, Pod, Zeroable, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
#[cfg(feature = "serde")]
#[derive(serde::Serialize, serde::Deserialize)]
#[repr(C)]
pub struct Record {
pub barcode: u64,
pub umi: u64,
pub index: u64,
}
impl Record {
pub fn new(barcode: u64, umi: u64, index: u64) -> Self {
Self {
barcode,
umi,
index,
}
}
pub fn as_bytes(&self) -> &[u8] {
bytemuck::bytes_of(self)
}
pub fn from_bytes(bytes: &[u8]) -> Self {
*bytemuck::from_bytes(bytes)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_record_creation() {
let record = Record::new(0x1234, 0x5678, 42);
assert_eq!(record.barcode, 0x1234);
assert_eq!(record.umi, 0x5678);
assert_eq!(record.index, 42);
}
#[test]
fn test_record_size() {
assert_eq!(RECORD_SIZE, 24);
assert_eq!(std::mem::size_of::<Record>(), RECORD_SIZE);
}
#[test]
fn test_record_default() {
let record = Record::default();
assert_eq!(record.barcode, 0);
assert_eq!(record.umi, 0);
assert_eq!(record.index, 0);
}
#[test]
fn test_record_ordering() {
let a = Record::new(0, 0, 0);
let b = Record::new(1, 0, 0);
assert!(a < b);
assert!(b > a);
let c = Record::new(0, 0, 0);
let d = Record::new(0, 1, 0);
assert!(c < d);
assert!(d > c);
let e = Record::new(0, 0, 0);
let f = Record::new(0, 0, 1);
assert!(e < f);
assert!(f > e);
}
#[test]
fn test_comprehensive_sorting() {
let mut records = vec![
Record::new(1, 1, 1),
Record::new(0, 1, 1),
Record::new(1, 0, 1),
Record::new(0, 0, 1),
Record::new(1, 1, 0),
Record::new(0, 1, 0),
Record::new(1, 0, 0),
Record::new(0, 0, 0),
];
records.sort();
let expected = vec![
Record::new(0, 0, 0),
Record::new(0, 0, 1),
Record::new(0, 1, 0),
Record::new(0, 1, 1),
Record::new(1, 0, 0),
Record::new(1, 0, 1),
Record::new(1, 1, 0),
Record::new(1, 1, 1),
];
assert_eq!(records, expected);
}
#[test]
fn test_lexicographic_ordering() {
let a = Record::new(0, 0, 0);
let b = Record::new(0, 0, 1);
let c = Record::new(0, 1, 0);
let d = Record::new(1, 0, 0);
let e = Record::new(1, 1, 0);
let f = Record::new(0, 1, 1);
let g = Record::new(1, 0, 1);
let h = Record::new(1, 1, 1);
assert!(a < b);
assert!(b < c);
assert!(c < d);
assert!(d < e);
assert!(e > f); assert!(f < g);
assert!(g < h);
}
#[test]
fn test_byte_conversion_roundtrip() {
let original = Record::new(0x123456789ABCDEF0, 0xFEDCBA9876543210, u64::MAX);
let bytes = original.as_bytes();
assert_eq!(bytes.len(), RECORD_SIZE);
let reconstructed = Record::from_bytes(bytes);
assert_eq!(original, reconstructed);
}
#[test]
fn test_byte_conversion_zero_values() {
let original = Record::new(0, 0, 0);
let bytes = original.as_bytes();
let reconstructed = Record::from_bytes(bytes);
assert_eq!(original, reconstructed);
assert_eq!(reconstructed.barcode, 0);
assert_eq!(reconstructed.umi, 0);
assert_eq!(reconstructed.index, 0);
}
#[test]
fn test_byte_conversion_max_values() {
let original = Record::new(u64::MAX, u64::MAX, u64::MAX);
let bytes = original.as_bytes();
let reconstructed = Record::from_bytes(bytes);
assert_eq!(original, reconstructed);
}
#[test]
fn test_record_derives() {
let record1 = Record::new(1, 2, 3);
let record2 = Record::new(1, 2, 3);
let record3 = Record::new(4, 5, 6);
assert_eq!(record1, record2);
assert_ne!(record1, record3);
let cloned = record1.clone();
assert_eq!(record1, cloned);
let copied = record1;
assert_eq!(record1, copied);
let debug_str = format!("{:?}", record1);
assert!(debug_str.contains("Record"));
assert!(debug_str.contains("barcode: 1"));
assert!(debug_str.contains("umi: 2"));
assert!(debug_str.contains("index: 3"));
use std::collections::HashMap;
let mut map = HashMap::new();
map.insert(record1, "value");
assert_eq!(map.get(&record2), Some(&"value"));
assert!(record1 < record3);
assert!(record3 > record1);
}
#[test]
fn test_equality() {
let record1 = Record::new(100, 200, 300);
let record2 = Record::new(100, 200, 300);
let record3 = Record::new(100, 200, 301);
assert_eq!(record1, record2);
assert_ne!(record1, record3);
assert_ne!(record2, record3);
}
#[test]
fn test_large_values() {
let max_32_bases = (1u64 << 32) - 1; let record = Record::new(max_32_bases, max_32_bases, u64::MAX);
assert_eq!(record.barcode, max_32_bases);
assert_eq!(record.umi, max_32_bases);
assert_eq!(record.index, u64::MAX);
}
}