use core::convert::TryFrom;
use paste::paste;
pub trait StepMode:
Into<u16> + TryFrom<u16, Error = InvalidStepModeError> + Copy
{
type Iter: Iterator<Item = Self>;
fn iter() -> Self::Iter;
}
macro_rules! generate_step_mode_enums {
(
$(
$max:expr => $($variant:expr),*;
)*
) => {
$(
generate_step_mode_enums!(@gen_enum,
(),
(),
(),
$max => $($variant,)*
);
)*
};
(
@gen_enum,
(),
(),
(),
$max:expr => $($input:expr,)*
) => {
generate_step_mode_enums!(
@gen_enum,
(
#[doc = "Full steps"]
Full = 1,
),
(
1 => Ok(Self::Full),
),
(
[<StepMode $max>]::Full,
),
$max => $($input,)*
);
};
(
@gen_enum,
(
$($variant_output:tt)*
),
(
$($try_from_output:tt)*
),
(
$($iter_output:tt)*
),
$max:expr => $variant:expr, $($input:expr,)*
) => {
generate_step_mode_enums!(
@gen_enum,
(
$($variant_output)*
#[doc = $variant " microsteps per full step"]
[<M $variant>] = $variant,
),
(
$($try_from_output)*
$variant => Ok(Self::[<M $variant>]),
),
(
$($iter_output)*
[<StepMode $max>]::[<M $variant>],
),
$max => $($input,)*
);
};
(
@gen_enum,
(
$($variant_output:tt)*
),
(
$($try_from_output:tt)*
),
(
$($iter_output:tt)*
),
$max:expr =>
) => {
paste! {
#[doc =
"Defines the microstepping mode for drivers with a resolution \
of up to " $max " microsteps"
]
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum [<StepMode $max>] {
$($variant_output)*
}
impl From<[<StepMode $max>]> for u16 {
fn from(step_mode: [<StepMode $max>]) -> Self {
step_mode as Self
}
}
impl TryFrom<u16> for [<StepMode $max>] {
type Error = InvalidStepModeError;
fn try_from(val: u16) -> Result<Self, Self::Error> {
match val {
$($try_from_output)*
_ => Err(InvalidStepModeError),
}
}
}
impl StepMode for [<StepMode $max>] {
type Iter = [<Iter $max>];
fn iter() -> Self::Iter {
[<Iter $max>] {
i: 0,
}
}
}
#[doc =
"An iterator over the variants of [`StepMode" $max "`]"
]
pub struct [<Iter $max>] {
i: usize,
}
impl Iterator for [<Iter $max>] {
type Item = [<StepMode $max>];
fn next(&mut self) -> Option<Self::Item> {
let modes = [$($iter_output)*];
if self.i < modes.len() {
let mode = modes[self.i];
self.i += 1;
Some(mode)
}
else {
None
}
}
}
}
};
}
generate_step_mode_enums! {
2 => 2;
4 => 2, 4;
8 => 2, 4, 8;
16 => 2, 4, 8, 16;
32 => 2, 4, 8, 16, 32;
64 => 2, 4, 8, 16, 32, 64;
128 => 2, 4, 8, 16, 32, 64, 128;
256 => 2, 4, 8, 16, 32, 64, 128, 256;
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct InvalidStepModeError;
#[cfg(test)]
mod tests {
use core::convert::TryFrom;
use super::{StepMode as _, StepMode256};
#[test]
fn step_mode_should_convert_into_microsteps_per_step() {
use StepMode256::*;
assert_eq!(<StepMode256 as Into<u16>>::into(Full), 1);
assert_eq!(<StepMode256 as Into<u16>>::into(M2), 2);
assert_eq!(<StepMode256 as Into<u16>>::into(M4), 4);
assert_eq!(<StepMode256 as Into<u16>>::into(M8), 8);
assert_eq!(<StepMode256 as Into<u16>>::into(M16), 16);
assert_eq!(<StepMode256 as Into<u16>>::into(M32), 32);
assert_eq!(<StepMode256 as Into<u16>>::into(M64), 64);
assert_eq!(<StepMode256 as Into<u16>>::into(M128), 128);
assert_eq!(<StepMode256 as Into<u16>>::into(M256), 256);
}
#[test]
fn step_mode_should_convert_from_microsteps_per_step() {
use StepMode256::*;
assert_eq!(<StepMode256 as TryFrom<u16>>::try_from(1), Ok(Full));
assert_eq!(<StepMode256 as TryFrom<u16>>::try_from(2), Ok(M2));
assert_eq!(<StepMode256 as TryFrom<u16>>::try_from(4), Ok(M4));
assert_eq!(<StepMode256 as TryFrom<u16>>::try_from(8), Ok(M8));
assert_eq!(<StepMode256 as TryFrom<u16>>::try_from(16), Ok(M16));
assert_eq!(<StepMode256 as TryFrom<u16>>::try_from(32), Ok(M32));
assert_eq!(<StepMode256 as TryFrom<u16>>::try_from(64), Ok(M64));
assert_eq!(<StepMode256 as TryFrom<u16>>::try_from(128), Ok(M128));
assert_eq!(<StepMode256 as TryFrom<u16>>::try_from(256), Ok(M256));
}
#[test]
fn step_mode_should_provide_iterator_over_modes() {
use StepMode256::*;
let modes: Vec<_> = StepMode256::iter().collect();
assert_eq!(modes, [Full, M2, M4, M8, M16, M32, M64, M128, M256]);
}
}