#[macro_export]
macro_rules! command {
(
$(#[$docs:meta])+
$cmd:ident {
command_type: $cmd_type:expr,
command_class: $cmd_class:expr,
command_index: $cmd_idx:expr,
argument: $arg:ident,
default_arg: $default_arg:expr,
response_type: $res_type:expr,
}
) => {
paste::paste! {
$crate::lib_bitfield! {
$(#[$docs])+
pub $cmd(MSB0 [u8; $crate::command::CMD_LEN]): u32 {
start_bit: 47;
direction: 46;
raw_command_index: 45, 40;
raw_argument: 39, 8;
raw_crc: 7, 1;
end_bit: 0;
}
}
impl $cmd {
#[doc = "Represents the byte length of the [" $cmd "]."]
pub const LEN: usize = $crate::command::CMD_LEN;
pub const COMMAND_INDEX: u8 = $cmd_idx;
pub const fn new() -> Self {
Self(Self::default_bytes())
}
#[doc = "Gets the start sequence of the [" $cmd "]."]
#[inline]
pub const fn start() -> u8 {
0x40 | Self::COMMAND_INDEX
}
#[inline]
const fn default_bytes() -> [u8; Self::LEN] {
let [a0, a1, a2, a3] = $default_arg;
let raw = [Self::start(), a0, a1, a2, a3];
let crc = $crate::crc::Crc7::calculate(&raw);
let [r0, r1, r2, r3, r4] = raw;
[r0, r1, r2, r3, r4, (crc.bits() << 1) | 1]
}
#[doc = "Gets the command type for the [" $cmd "]."]
#[inline]
pub const fn command_type(&self) -> $crate::command::CommandType {
$cmd_type
}
#[doc = "Gets the command class for the [" $cmd "]."]
#[inline]
pub const fn command_class(&self) -> $crate::command::CommandClass {
$cmd_class
}
#[doc = "Gets the command indnex field of the [" $cmd "]."]
pub const fn command_index(&self) -> u8 {
self.raw_command_index() as u8
}
#[doc = "Gets the raw `argument` field of the [" $cmd "]."]
pub const fn argument_bits(&self) -> u32 {
self.raw_argument()
}
#[doc = "Gets the `argument` field of the [" $cmd "]."]
pub const fn argument(&self) -> $crate::result::Result<$arg> {
$arg::try_from_bits(self.raw_argument())
}
#[doc = "Sets the `argument` field of the [" $cmd "]."]
pub fn set_argument(&mut self, val: $arg) {
self.set_raw_argument(val.bits())
}
#[doc = "Gets the `CRC7` field of the [" $cmd "]."]
pub const fn crc(&self) -> $crate::crc::Crc7 {
$crate::crc::Crc7::from_bits(self.raw_crc() as u8)
}
#[doc = "Calculates and sets the `CRC7` field of the [" $cmd "]."]
pub fn calculate_crc(&mut self) -> $crate::crc::Crc7 {
let crc = $crate::crc::Crc7::calculate(self.0[..Self::LEN - 1].as_ref());
self.set_raw_crc(crc.bits() as u32);
crc
}
#[doc = "Gets the expected response type for the [" $cmd "]."]
#[inline]
pub const fn response_type(&self) -> $crate::response::ResponseType {
$res_type
}
#[doc = "Attempts to convert a byte slice into a [" $cmd "]."]
pub const fn try_from_bytes(val: &[u8]) -> $crate::result::Result<Self> {
use $crate::result::Error;
match val.len() {
len if len < Self::LEN => Err(Error::invalid_length(len, Self::LEN)),
_ => {
let crc = $crate::crc::Crc7::calculate(&[val[0], val[1], val[2], val[3], val[4]]);
match Self([val[0], val[1], val[2], val[3], val[4], val[5]]) {
cmd if cmd.start_bit() => Err(Error::invalid_field_variant("cmd::start_bit", 1)),
cmd if !cmd.direction() => Err(Error::invalid_field_variant("cmd::direction", 0)),
cmd if cmd.command_index() != Self::COMMAND_INDEX => Err(Error::invalid_field_variant(
"cmd::command_index",
cmd.command_index() as usize,
)),
cmd if cmd.argument().is_err() => Err(Error::invalid_field_variant(
"cmd::argument",
cmd.raw_argument() as usize,
)),
cmd if cmd.raw_crc() as u8 != crc.bits() => {
Err(Error::invalid_crc7(cmd.raw_crc() as u8, crc.bits()))
}
cmd if !cmd.end_bit() => Err(Error::invalid_field_variant("cmd::end_bit", 0)),
cmd => Ok(cmd),
}
}
}
}
}
impl Default for $cmd {
fn default() -> Self {
Self::new()
}
}
impl From<$cmd> for [u8; $cmd::LEN] {
fn from(val: $cmd) -> Self {
val.bytes()
}
}
impl TryFrom<&[u8]> for $cmd {
type Error = $crate::result::Error;
fn try_from(val: &[u8]) -> $crate::result::Result<Self> {
Self::try_from_bytes(val)
}
}
impl<const N: usize> TryFrom<&[u8; N]> for $cmd {
type Error = $crate::result::Error;
fn try_from(val: &[u8; N]) -> $crate::result::Result<Self> {
Self::try_from_bytes(val.as_ref())
}
}
impl<const N: usize> TryFrom<[u8; N]> for $cmd {
type Error = $crate::result::Error;
fn try_from(val: [u8; N]) -> $crate::result::Result<Self> {
Self::try_from_bytes(val.as_ref())
}
}
#[cfg(test)]
pub mod tests {
use super::*;
use $crate::util::raw_with_crc;
#[test]
fn test_fields() {
let new_arg = $arg::new();
(1..=u32::BITS).map(|r| ((1u64 << r) - 1) as u32).for_each(|raw_arg| {
let start = $cmd::start();
let [arg0, arg1, arg2, arg3] = raw_arg.to_be_bytes();
let (raw, crc) = raw_with_crc([start, arg0, arg1, arg2, arg3, 0]);
match $arg::try_from_bits(raw_arg) {
Ok(exp_arg) => {
let mut exp_cmd = $cmd(raw);
assert_eq!($cmd::try_from_bytes(&raw), Ok(exp_cmd));
assert_eq!($cmd::try_from(&raw), Ok(exp_cmd));
assert_eq!(exp_cmd.bytes(), raw);
assert_eq!(exp_cmd.command_index(), $cmd::COMMAND_INDEX);
assert_eq!(exp_cmd.argument(), Ok(exp_arg));
assert_eq!(exp_cmd.crc(), crc);
exp_cmd.set_argument(new_arg);
assert_eq!(exp_cmd.argument(), Ok(new_arg));
exp_cmd.set_argument(exp_arg);
assert_eq!(exp_cmd.argument(), Ok(exp_arg));
}
Err(_err) => {
let exp_err = Err($crate::result::Error::invalid_field_variant("cmd::argument", raw_arg as usize));
assert_eq!($cmd::try_from_bytes(&raw), exp_err);
assert_eq!($cmd::try_from(&raw), exp_err);
}
}
});
}
}
}
};
}
#[macro_export]
macro_rules! command_enum {
(
$(#[$doc:meta])*
$cmd:ident {
default: $default_cmd:ident ($default_cmd_ty:expr),
$(
$(#[$doc_var:meta])*
$cmd_var:ident ($cmd_mod:ident :: $cmd_inner:ident),
)+
}
) => {
$(#[$doc])*
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum $cmd {
$(
$(#[$doc_var])*
$cmd_var($cmd_mod::$cmd_inner),
)+
}
::paste::paste! {
impl $cmd {
#[doc = "Represents the byte length of the [" $cmd "]."]
pub const LEN: usize = 6;
#[doc = "Creates a new [" $cmd "]."]
pub const fn new() -> Self {
Self::$default_cmd($default_cmd_ty::new())
}
#[doc = "Gets the command index for the [" $cmd "]."]
pub const fn command_index(&self) -> u8 {
match self {
$(
Self::$cmd_var(cmd) => cmd.command_index(),
)+
}
}
#[doc = "Gets the command type for the [" $cmd "]."]
pub const fn command_type(&self) -> $crate::command::CommandType {
match self {
$(
Self::$cmd_var(cmd) => cmd.command_type(),
)+
}
}
#[doc = "Gets the response type for the [" $cmd "]."]
pub const fn response_type(&self) -> $crate::response::ResponseType {
match self {
$(
Self::$cmd_var(cmd) => cmd.response_type(),
)+
}
}
#[doc = "Gets the raw command argument value for the [" $cmd "]."]
pub fn argument(&self) -> $crate::result::Result<u32> {
match self {
$(
Self::$cmd_var(cmd) => cmd.argument().map(|arg| arg.bits()),
)+
}
}
#[doc = "Gets the `CRC7` field of the [" $cmd "]."]
pub const fn crc(&self) -> $crate::crc::Crc7 {
match self {
$(
Self::$cmd_var(cmd) => cmd.crc(),
)+
}
}
#[doc = "Converts the [" $cmd "] into a byte array."]
pub const fn bytes(&self) -> [u8; Self::LEN] {
match self {
$(
Self::$cmd_var(cmd) => cmd.bytes(),
)+
}
}
}
impl Default for $cmd {
fn default() -> Self {
Self::new()
}
}
$(
impl From<$cmd_mod::$cmd_inner> for $cmd {
fn from(val: $cmd_mod::$cmd_inner) -> Self {
Self::$cmd_var(val)
}
}
impl TryFrom<$cmd> for $cmd_mod::$cmd_inner {
type Error = $crate::result::Error;
fn try_from(val: $cmd) -> $crate::result::Result<Self> {
match val {
$cmd::$cmd_var(cmd) => Ok(cmd),
_ => Err(Self::Error::invalid_field_variant("command", val.command_index() as usize)),
}
}
}
)+
}
};
}