1#[macro_export]
3macro_rules! command {
4 (
5 $(#[$docs:meta])+
6 $cmd:ident {
7 command_type: $cmd_type:expr,
8 command_class: $cmd_class:expr,
9 command_index: $cmd_idx:expr,
10 argument: $arg:ident,
11 default_arg: $default_arg:expr,
12 response_type: $res_type:expr,
13 }
14 ) => {
15 paste::paste! {
16 $crate::lib_bitfield! {
17 $(#[$docs])+
18 pub $cmd(MSB0 [u8; $crate::command::CMD_LEN]): u32 {
19 start_bit: 47;
20 direction: 46;
21 raw_command_index: 45, 40;
22 raw_argument: 39, 8;
23 raw_crc: 7, 1;
24 end_bit: 0;
25 }
26 }
27
28 impl $cmd {
29 #[doc = "Represents the byte length of the [" $cmd "]."]
30 pub const LEN: usize = $crate::command::CMD_LEN;
31 pub const COMMAND_INDEX: u8 = $cmd_idx;
33
34 pub const fn new() -> Self {
36 Self(Self::default_bytes())
37 }
38
39 #[doc = "Gets the start sequence of the [" $cmd "]."]
40 #[inline]
41 pub const fn start() -> u8 {
42 0x40 | Self::COMMAND_INDEX
43 }
44
45 #[inline]
46 const fn default_bytes() -> [u8; Self::LEN] {
47 let [a0, a1, a2, a3] = $default_arg;
48 let raw = [Self::start(), a0, a1, a2, a3];
49 let crc = $crate::crc::Crc7::calculate(&raw);
50 let [r0, r1, r2, r3, r4] = raw;
51
52 [r0, r1, r2, r3, r4, (crc.bits() << 1) | 1]
53 }
54
55 #[doc = "Gets the command type for the [" $cmd "]."]
56 #[inline]
57 pub const fn command_type(&self) -> $crate::command::CommandType {
58 $cmd_type
59 }
60
61 #[doc = "Gets the command class for the [" $cmd "]."]
62 #[inline]
63 pub const fn command_class(&self) -> $crate::command::CommandClass {
64 $cmd_class
65 }
66
67 #[doc = "Gets the command indnex field of the [" $cmd "]."]
68 pub const fn command_index(&self) -> u8 {
69 self.raw_command_index() as u8
70 }
71
72 #[doc = "Gets the raw `argument` field of the [" $cmd "]."]
73 pub const fn argument_bits(&self) -> u32 {
74 self.raw_argument()
75 }
76
77 #[doc = "Gets the `argument` field of the [" $cmd "]."]
78 pub const fn argument(&self) -> $crate::result::Result<$arg> {
79 $arg::try_from_bits(self.raw_argument())
80 }
81
82 #[doc = "Sets the `argument` field of the [" $cmd "]."]
83 pub fn set_argument(&mut self, val: $arg) {
84 self.set_raw_argument(val.bits())
85 }
86
87 #[doc = "Gets the `CRC7` field of the [" $cmd "]."]
88 pub const fn crc(&self) -> $crate::crc::Crc7 {
89 $crate::crc::Crc7::from_bits(self.raw_crc() as u8)
90 }
91
92 #[doc = "Calculates and sets the `CRC7` field of the [" $cmd "]."]
93 pub fn calculate_crc(&mut self) -> $crate::crc::Crc7 {
94 let crc = $crate::crc::Crc7::calculate(self.0[..Self::LEN - 1].as_ref());
95 self.set_raw_crc(crc.bits() as u32);
96 crc
97 }
98
99 #[doc = "Gets the expected response type for the [" $cmd "]."]
100 #[inline]
101 pub const fn response_type(&self) -> $crate::response::ResponseType {
102 $res_type
103 }
104
105 #[doc = "Attempts to convert a byte slice into a [" $cmd "]."]
106 pub const fn try_from_bytes(val: &[u8]) -> $crate::result::Result<Self> {
107 use $crate::result::Error;
108
109 match val.len() {
110 len if len < Self::LEN => Err(Error::invalid_length(len, Self::LEN)),
111 _ => {
112 let crc = $crate::crc::Crc7::calculate(&[val[0], val[1], val[2], val[3], val[4]]);
113
114 match Self([val[0], val[1], val[2], val[3], val[4], val[5]]) {
115 cmd if cmd.start_bit() => Err(Error::invalid_field_variant("cmd::start_bit", 1)),
116 cmd if !cmd.direction() => Err(Error::invalid_field_variant("cmd::direction", 0)),
117 cmd if cmd.command_index() != Self::COMMAND_INDEX => Err(Error::invalid_field_variant(
118 "cmd::command_index",
119 cmd.command_index() as usize,
120 )),
121 cmd if cmd.argument().is_err() => Err(Error::invalid_field_variant(
122 "cmd::argument",
123 cmd.raw_argument() as usize,
124 )),
125 cmd if cmd.raw_crc() as u8 != crc.bits() => {
126 Err(Error::invalid_crc7(cmd.raw_crc() as u8, crc.bits()))
127 }
128 cmd if !cmd.end_bit() => Err(Error::invalid_field_variant("cmd::end_bit", 0)),
129 cmd => Ok(cmd),
130 }
131 }
132 }
133 }
134 }
135
136 impl Default for $cmd {
137 fn default() -> Self {
138 Self::new()
139 }
140 }
141
142 impl From<$cmd> for [u8; $cmd::LEN] {
143 fn from(val: $cmd) -> Self {
144 val.bytes()
145 }
146 }
147
148 impl TryFrom<&[u8]> for $cmd {
149 type Error = $crate::result::Error;
150
151 fn try_from(val: &[u8]) -> $crate::result::Result<Self> {
152 Self::try_from_bytes(val)
153 }
154 }
155
156 impl<const N: usize> TryFrom<&[u8; N]> for $cmd {
157 type Error = $crate::result::Error;
158
159 fn try_from(val: &[u8; N]) -> $crate::result::Result<Self> {
160 Self::try_from_bytes(val.as_ref())
161 }
162 }
163
164 impl<const N: usize> TryFrom<[u8; N]> for $cmd {
165 type Error = $crate::result::Error;
166
167 fn try_from(val: [u8; N]) -> $crate::result::Result<Self> {
168 Self::try_from_bytes(val.as_ref())
169 }
170 }
171
172 #[cfg(test)]
173 pub mod tests {
174 use super::*;
175 use $crate::util::raw_with_crc;
176
177 #[test]
178 fn test_fields() {
179 let new_arg = $arg::new();
180
181 (1..=u32::BITS).map(|r| ((1u64 << r) - 1) as u32).for_each(|raw_arg| {
182 let start = $cmd::start();
183 let [arg0, arg1, arg2, arg3] = raw_arg.to_be_bytes();
184 let (raw, crc) = raw_with_crc([start, arg0, arg1, arg2, arg3, 0]);
185
186 match $arg::try_from_bits(raw_arg) {
187 Ok(exp_arg) => {
188
189 let mut exp_cmd = $cmd(raw);
190
191 assert_eq!($cmd::try_from_bytes(&raw), Ok(exp_cmd));
192 assert_eq!($cmd::try_from(&raw), Ok(exp_cmd));
193
194 assert_eq!(exp_cmd.bytes(), raw);
195 assert_eq!(exp_cmd.command_index(), $cmd::COMMAND_INDEX);
196 assert_eq!(exp_cmd.argument(), Ok(exp_arg));
197 assert_eq!(exp_cmd.crc(), crc);
198
199 exp_cmd.set_argument(new_arg);
200 assert_eq!(exp_cmd.argument(), Ok(new_arg));
201
202 exp_cmd.set_argument(exp_arg);
203 assert_eq!(exp_cmd.argument(), Ok(exp_arg));
204 }
205 Err(_err) => {
206 let exp_err = Err($crate::result::Error::invalid_field_variant("cmd::argument", raw_arg as usize));
207
208 assert_eq!($cmd::try_from_bytes(&raw), exp_err);
209 assert_eq!($cmd::try_from(&raw), exp_err);
210 }
211 }
212 });
213 }
214 }
215 }
216 };
217}
218
219#[macro_export]
221macro_rules! command_enum {
222 (
223 $(#[$doc:meta])*
224 $cmd:ident {
225 default: $default_cmd:ident ($default_cmd_ty:expr),
226 $(
227 $(#[$doc_var:meta])*
228 $cmd_var:ident ($cmd_mod:ident :: $cmd_inner:ident),
229 )+
230 }
231 ) => {
232 $(#[$doc])*
233 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
234 pub enum $cmd {
235 $(
236 $(#[$doc_var])*
237 $cmd_var($cmd_mod::$cmd_inner),
238 )+
239 }
240
241 ::paste::paste! {
242 impl $cmd {
243 #[doc = "Represents the byte length of the [" $cmd "]."]
244 pub const LEN: usize = 6;
245
246 #[doc = "Creates a new [" $cmd "]."]
247 pub const fn new() -> Self {
248 Self::$default_cmd($default_cmd_ty::new())
249 }
250
251 #[doc = "Gets the command index for the [" $cmd "]."]
252 pub const fn command_index(&self) -> u8 {
253 match self {
254 $(
255 Self::$cmd_var(cmd) => cmd.command_index(),
256 )+
257 }
258 }
259
260 #[doc = "Gets the command type for the [" $cmd "]."]
261 pub const fn command_type(&self) -> $crate::command::CommandType {
262 match self {
263 $(
264 Self::$cmd_var(cmd) => cmd.command_type(),
265 )+
266 }
267 }
268
269 #[doc = "Gets the response type for the [" $cmd "]."]
270 pub const fn response_type(&self) -> $crate::response::ResponseType {
271 match self {
272 $(
273 Self::$cmd_var(cmd) => cmd.response_type(),
274 )+
275 }
276 }
277
278 #[doc = "Gets the raw command argument value for the [" $cmd "]."]
279 pub fn argument(&self) -> $crate::result::Result<u32> {
280 match self {
281 $(
282 Self::$cmd_var(cmd) => cmd.argument().map(|arg| arg.bits()),
283 )+
284 }
285 }
286
287 #[doc = "Gets the `CRC7` field of the [" $cmd "]."]
288 pub const fn crc(&self) -> $crate::crc::Crc7 {
289 match self {
290 $(
291 Self::$cmd_var(cmd) => cmd.crc(),
292 )+
293 }
294 }
295
296 #[doc = "Converts the [" $cmd "] into a byte array."]
297 pub const fn bytes(&self) -> [u8; Self::LEN] {
298 match self {
299 $(
300 Self::$cmd_var(cmd) => cmd.bytes(),
301 )+
302 }
303 }
304 }
305
306 impl Default for $cmd {
307 fn default() -> Self {
308 Self::new()
309 }
310 }
311
312 $(
313 impl From<$cmd_mod::$cmd_inner> for $cmd {
314 fn from(val: $cmd_mod::$cmd_inner) -> Self {
315 Self::$cmd_var(val)
316 }
317 }
318
319 impl TryFrom<$cmd> for $cmd_mod::$cmd_inner {
320 type Error = $crate::result::Error;
321
322 fn try_from(val: $cmd) -> $crate::result::Result<Self> {
323 match val {
324 $cmd::$cmd_var(cmd) => Ok(cmd),
325 _ => Err(Self::Error::invalid_field_variant("command", val.command_index() as usize)),
326 }
327 }
328 }
329 )+
330 }
331 };
332}