stepper/
step_mode.rs

1//! Types related to working with a driver's microstepping mode
2
3use core::convert::TryFrom;
4
5use paste::paste;
6
7/// Implemented for all step mode enums
8pub trait StepMode:
9    Into<u16> + TryFrom<u16, Error = InvalidStepModeError> + Copy
10{
11    /// The type of the iterator returned by [`StepMode::iter`]
12    type Iter: Iterator<Item = Self>;
13
14    /// Returns an iterator over all supported modes
15    ///
16    /// Starts at the mode for configuring full steps and ends at the highest
17    /// supported number of microsteps per step.
18    fn iter() -> Self::Iter;
19}
20
21macro_rules! generate_step_mode_enums {
22    (
23        $(
24            $max:expr => $($variant:expr),*;
25        )*
26    ) => {
27        $(
28            generate_step_mode_enums!(@gen_enum,
29                (),
30                (),
31                (),
32                $max => $($variant,)*
33            );
34        )*
35    };
36
37    // This is a trick to work around a limitation of Rust macros: We can't
38    // generate a part of something, like an enum variant. We can only generate
39    // complete things, like the whole enum.
40    //
41    // This first rules gets matched on the first call, when there is not output
42    // yet. It is generates the full step variant, then passes the input on to
43    // the next macro call.
44    (
45        @gen_enum,
46        (),
47        (),
48        (),
49        $max:expr => $($input:expr,)*
50    ) => {
51        generate_step_mode_enums!(
52            @gen_enum,
53            (
54                #[doc = "Full steps"]
55                Full = 1,
56            ),
57            (
58                1 => Ok(Self::Full),
59            ),
60            (
61                [<StepMode $max>]::Full,
62            ),
63            $max => $($input,)*
64        );
65    };
66    // This next rule gets matched as long as there are still enum variants to
67    // be generated. It creates the tokens that make up a variant, then passes
68    // them and the rest of the input on to the next recursive macro call.
69    (
70        @gen_enum,
71        (
72            $($variant_output:tt)*
73        ),
74        (
75            $($try_from_output:tt)*
76        ),
77        (
78            $($iter_output:tt)*
79        ),
80        $max:expr => $variant:expr, $($input:expr,)*
81    ) => {
82        generate_step_mode_enums!(
83            @gen_enum,
84            (
85                $($variant_output)*
86
87                #[doc = $variant " microsteps per full step"]
88                [<M $variant>] = $variant,
89            ),
90            (
91                $($try_from_output)*
92
93                $variant => Ok(Self::[<M $variant>]),
94            ),
95            (
96                $($iter_output)*
97
98                [<StepMode $max>]::[<M $variant>],
99            ),
100            $max => $($input,)*
101        );
102    };
103    // This last rule gets matched when there is no more input left and all
104    // variants have been generated. It takes all the tokens generated in
105    // previous macro calls and uses them to generate the complete enum.
106    (
107        @gen_enum,
108        (
109            $($variant_output:tt)*
110        ),
111        (
112            $($try_from_output:tt)*
113        ),
114        (
115            $($iter_output:tt)*
116        ),
117        $max:expr =>
118    ) => {
119        paste! {
120            #[doc =
121                "Defines the microstepping mode for drivers with a resolution \
122                of up to " $max " microsteps"
123            ]
124            #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
125            pub enum [<StepMode $max>] {
126                $($variant_output)*
127            }
128
129            impl From<[<StepMode $max>]> for u16 {
130                fn from(step_mode: [<StepMode $max>]) -> Self {
131                    step_mode as Self
132                }
133            }
134
135            impl TryFrom<u16> for [<StepMode $max>] {
136                type Error = InvalidStepModeError;
137
138                fn try_from(val: u16) -> Result<Self, Self::Error> {
139                    match val {
140                        $($try_from_output)*
141
142                        _ => Err(InvalidStepModeError),
143                    }
144                }
145            }
146
147            impl StepMode for [<StepMode $max>] {
148                // It would be nice to avoid the custom iterator and use
149                // `iter::from_fn` instead. That would require `impl Iterator`
150                // here, which is not supported yet. Tracking issue:
151                // https://github.com/rust-lang/rust/issues/63063
152                type Iter = [<Iter $max>];
153
154                fn iter() -> Self::Iter {
155                    [<Iter $max>] {
156                        i: 0,
157                    }
158                }
159            }
160
161            #[doc =
162                "An iterator over the variants of [`StepMode" $max "`]"
163            ]
164            pub struct [<Iter $max>] {
165                i: usize,
166            }
167
168            impl Iterator for [<Iter $max>] {
169                type Item = [<StepMode $max>];
170
171                fn next(&mut self) -> Option<Self::Item> {
172                    let modes = [$($iter_output)*];
173
174                    if self.i < modes.len() {
175                        let mode = modes[self.i];
176                        self.i += 1;
177                        Some(mode)
178                    }
179                    else {
180                        None
181                    }
182                }
183            }
184        }
185    };
186}
187
188generate_step_mode_enums! {
189    2   => 2;
190    4   => 2, 4;
191    8   => 2, 4, 8;
192    16  => 2, 4, 8, 16;
193    32  => 2, 4, 8, 16, 32;
194    64  => 2, 4, 8, 16, 32, 64;
195    128 => 2, 4, 8, 16, 32, 64, 128;
196    256 => 2, 4, 8, 16, 32, 64, 128, 256;
197}
198
199/// Indicates that a given step mode value did not represent a valid step mode
200///
201/// Returned by the `TryFrom` implementations of the various step mode enums.
202#[derive(Clone, Copy, Debug, Eq, PartialEq)]
203pub struct InvalidStepModeError;
204
205#[cfg(test)]
206mod tests {
207    // Only tests `StepMode256`. This should be fine, since all other step mode
208    // enums are generated by the same code.
209
210    use core::convert::TryFrom;
211
212    use super::{StepMode as _, StepMode256};
213
214    #[test]
215    fn step_mode_should_convert_into_microsteps_per_step() {
216        use StepMode256::*;
217
218        assert_eq!(<StepMode256 as Into<u16>>::into(Full), 1);
219        assert_eq!(<StepMode256 as Into<u16>>::into(M2), 2);
220        assert_eq!(<StepMode256 as Into<u16>>::into(M4), 4);
221        assert_eq!(<StepMode256 as Into<u16>>::into(M8), 8);
222        assert_eq!(<StepMode256 as Into<u16>>::into(M16), 16);
223        assert_eq!(<StepMode256 as Into<u16>>::into(M32), 32);
224        assert_eq!(<StepMode256 as Into<u16>>::into(M64), 64);
225        assert_eq!(<StepMode256 as Into<u16>>::into(M128), 128);
226        assert_eq!(<StepMode256 as Into<u16>>::into(M256), 256);
227    }
228
229    #[test]
230    fn step_mode_should_convert_from_microsteps_per_step() {
231        use StepMode256::*;
232
233        assert_eq!(<StepMode256 as TryFrom<u16>>::try_from(1), Ok(Full));
234        assert_eq!(<StepMode256 as TryFrom<u16>>::try_from(2), Ok(M2));
235        assert_eq!(<StepMode256 as TryFrom<u16>>::try_from(4), Ok(M4));
236        assert_eq!(<StepMode256 as TryFrom<u16>>::try_from(8), Ok(M8));
237        assert_eq!(<StepMode256 as TryFrom<u16>>::try_from(16), Ok(M16));
238        assert_eq!(<StepMode256 as TryFrom<u16>>::try_from(32), Ok(M32));
239        assert_eq!(<StepMode256 as TryFrom<u16>>::try_from(64), Ok(M64));
240        assert_eq!(<StepMode256 as TryFrom<u16>>::try_from(128), Ok(M128));
241        assert_eq!(<StepMode256 as TryFrom<u16>>::try_from(256), Ok(M256));
242    }
243
244    #[test]
245    fn step_mode_should_provide_iterator_over_modes() {
246        use StepMode256::*;
247
248        let modes: Vec<_> = StepMode256::iter().collect();
249        assert_eq!(modes, [Full, M2, M4, M8, M16, M32, M64, M128, M256]);
250    }
251}