scsir 0.3.0

A simple library for issuing SCSI commands
Documentation
#![allow(dead_code)]

use std::mem::size_of_val;

use modular_bitfield_msb::prelude::*;

use crate::command::get_array;

#[bitfield]
#[derive(Clone, Copy, Debug)]
pub struct ShortHeader {
    pub mode_data_length: B8,
    pub medium_type: B8,
    pub write_protect: B1,
    reserved_0: B2,
    pub dpo_and_fua_support: B1,
    reserved_1: B4,
    pub block_descriptor_length: B8,
}

#[bitfield]
#[derive(Clone, Copy, Debug)]
pub struct LongHeader {
    pub mode_data_length: B16,
    pub medium_type: B8,
    pub write_protect: B1,
    reserved_0: B2,
    pub dpo_and_fua_support: B1,
    reserved_1: B4,
    reserved_2: B7,
    pub long_lba: B1,
    reserved_3: B8,
    pub block_descriptor_length: B16,
}

#[derive(Clone, Copy, Debug)]
pub enum HeaderType {
    Short,
    Long,
}

#[derive(Clone, Copy, Debug)]
pub enum HeaderStorage {
    Short(ShortHeader),
    Long(LongHeader),
}

impl HeaderStorage {
    pub fn from_bytes(header_type: HeaderType, bytes: &[u8]) -> (Self, &[u8]) {
        match header_type {
            HeaderType::Short => {
                let (array, bytes) = get_array(bytes);
                (HeaderStorage::Short(ShortHeader::from_bytes(array)), bytes)
            }
            HeaderType::Long => {
                let (array, bytes) = get_array(bytes);
                (HeaderStorage::Long(LongHeader::from_bytes(array)), bytes)
            }
        }
    }

    pub fn required_allocation_length(&self) -> u16 {
        match self {
            HeaderStorage::Short(h) => (h.mode_data_length() as u16)
                .saturating_add(size_of_val(&h.mode_data_length()) as u16),
            HeaderStorage::Long(h) => h
                .mode_data_length()
                .saturating_add(size_of_val(&h.mode_data_length()) as u16),
        }
    }

    pub fn mode_data_length(&self) -> u16 {
        match self {
            HeaderStorage::Short(h) => h.mode_data_length() as u16,
            HeaderStorage::Long(h) => h.mode_data_length(),
        }
    }

    pub fn medium_type(&self) -> u8 {
        match self {
            HeaderStorage::Short(h) => h.medium_type(),
            HeaderStorage::Long(h) => h.medium_type(),
        }
    }

    pub fn write_protect(&self) -> bool {
        match self {
            HeaderStorage::Short(h) => h.write_protect() != 0,
            HeaderStorage::Long(h) => h.write_protect() != 0,
        }
    }

    pub fn dpo_and_fua_support(&self) -> bool {
        match self {
            HeaderStorage::Short(h) => h.dpo_and_fua_support() != 0,
            HeaderStorage::Long(h) => h.dpo_and_fua_support() != 0,
        }
    }

    pub fn long_lba(&self) -> bool {
        match self {
            HeaderStorage::Short(_) => false,
            HeaderStorage::Long(h) => h.long_lba() != 0,
        }
    }

    pub fn block_descriptor_length(&self) -> u16 {
        match self {
            HeaderStorage::Short(h) => h.block_descriptor_length() as u16,
            HeaderStorage::Long(h) => h.block_descriptor_length(),
        }
    }

    pub fn to_bytes(&self) -> Vec<u8> {
        match self {
            HeaderStorage::Short(h) => h.bytes.to_vec(),
            HeaderStorage::Long(h) => h.bytes.to_vec(),
        }
    }
}

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

    const SHORT_HEADER_LENGTH: usize = 4;
    const LONG_HEADER_LENGTH: usize = 8;

    #[test]
    fn layout_test() {
        assert_eq!(
            size_of::<ShortHeader>(),
            SHORT_HEADER_LENGTH,
            concat!("Size of: ", stringify!(ShortHeader))
        );

        assert_eq!(
            size_of::<LongHeader>(),
            LONG_HEADER_LENGTH,
            concat!("Size of: ", stringify!(LongHeader))
        );
    }
}