tpm2_protocol/macro/
enum.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2// Copyright (c) 2025 Opinsys Oy
3// Copyright (c) 2024-2025 Jarkko Sakkinen
4
5//! The chain of `if`-statements is a deliberate design choice as patterns in
6//! a `match`-statement is too restricted for arbitrary expressions (e.g, see
7//! `TpmRc` for an example).
8
9#[macro_export]
10macro_rules! tpm_enum {
11    (
12        $(#[$enum_meta:meta])*
13        $vis:vis enum $name:ident($repr:ty) {
14            $(
15                $(#[$variant_meta:meta])*
16                ($variant:ident, $value:expr, $display:literal)
17            ),* $(,)?
18        }
19    ) => {
20        $(#[$enum_meta])*
21        #[repr($repr)]
22        $vis enum $name {
23            $(
24                $(#[$variant_meta])*
25                $variant = $value
26            ),*
27        }
28
29        impl TryFrom<$repr> for $name {
30            type Error = ();
31
32            #[allow(clippy::cognitive_complexity)]
33            fn try_from(value: $repr) -> Result<Self, ()> {
34                $(
35                    if value == $value {
36                        return Ok(Self::$variant);
37                    }
38                )*
39                Err(())
40            }
41        }
42
43        impl core::fmt::Display for $name {
44            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
45                let s = match self {
46                    $(Self::$variant => $display),*
47                };
48                write!(f, "{}", s)
49            }
50        }
51
52        impl core::str::FromStr for $name {
53            type Err = ();
54
55            fn from_str(s: &str) -> Result<Self, Self::Err> {
56                match s {
57                    $($display => Ok(Self::$variant),)*
58                    _ => Err(()),
59                }
60            }
61        }
62
63        impl $crate::TpmSized for $name {
64            const SIZE: usize = core::mem::size_of::<$repr>();
65
66            fn len(&self) -> usize {
67                Self::SIZE
68            }
69        }
70
71        impl $crate::TpmBuild for $name {
72            fn build(&self, writer: &mut $crate::TpmWriter) -> $crate::TpmResult<()> {
73                $crate::TpmBuild::build(&(*self as $repr), writer)
74            }
75        }
76
77        impl $crate::TpmParse for $name {
78            fn parse(buf: &[u8]) -> $crate::TpmResult<(Self, &[u8])> {
79                let (val, buf) = <$repr>::parse(buf)?;
80                let enum_val = Self::try_from(val).map_err(|()| $crate::TpmError::UnknownDiscriminant (stringify!($name), val.into()))?;
81                Ok((enum_val, buf))
82            }
83        }
84    };
85}