Keystore 2.0: Refactor permissions. 2/5

Move implement_permission macro to libkeystore2_selinux.

Test: keystore2_test
Bug: 203555519

Change-Id: I85d2411872aecaaa12876f848e9205431a8b0fa4
diff --git a/keystore2/selinux/src/lib.rs b/keystore2/selinux/src/lib.rs
index 902e9a4..aeb0e15 100644
--- a/keystore2/selinux/src/lib.rs
+++ b/keystore2/selinux/src/lib.rs
@@ -333,6 +333,241 @@
     }
 }
 
+/// This macro implements an enum with values mapped to SELinux permission names.
+/// The below example wraps the enum MyPermission in the tuple struct `MyPerm` and implements
+///  * From<i32> and Into<i32> are implemented. Where the implementation of From maps
+///    any variant not specified to the default.
+///  * Every variant has a constructor with a name corresponding to its lower case SELinux string
+///    representation.
+///  * `MyPermission::to_selinux(&self)` returns the SELinux string representation of the
+///    corresponding permission.
+///  * An implicit default values `MyPermission::None` is created with a numeric representation
+///    of `0` and a string representation of `"none"`.
+///  * Specifying a value is optional. If the value is omitted it is set to the value of the
+///    previous variant left shifted by 1.
+///
+/// ## Example
+/// ```
+/// implement_permission!(
+///     /// MyPermission documentation.
+///     #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+///     pub enum MyPermission {
+///         #[selinux(name = foo)]
+///         Foo = 1,
+///         #[selinux(name = bar)]
+///         Bar = 2,
+///         #[selinux(name = snafu)]
+///         Snafu, // Implicit value: MyPermission::Bar << 1 = 4
+///     }
+/// );
+/// ```
+#[macro_export]
+macro_rules! implement_permission {
+    (
+        $(#[$enum_meta:meta])*
+        $enum_vis:vis enum $enum_name:ident {
+            $(
+                $(#[$($emeta:tt)+])*
+                $vname:ident$( = $vval:tt)?
+            ),* $(,)?
+        }
+    ) => {
+        implement_permission!{
+            @extract_attr
+            $(#[$enum_meta])*
+            $enum_vis enum $enum_name {
+                1
+                []
+                [$(
+                    [] [$(#[$($emeta)+])*]
+                    $vname$( = $vval)?,
+                )*]
+            }
+        }
+    };
+
+    (
+        @extract_attr
+        $(#[$enum_meta:meta])*
+        $enum_vis:vis enum $enum_name:ident {
+            $next_val:tt
+            [$($out:tt)*]
+            [
+                [$(#[$mout:meta])*]
+                [
+                    #[selinux(name = $selinux_name:ident)]
+                    $(#[$($mtail:tt)+])*
+                ]
+                $vname:ident = $vval:tt,
+                $($tail:tt)*
+            ]
+        }
+    ) => {
+        implement_permission!{
+            @extract_attr
+            $(#[$enum_meta])*
+            $enum_vis enum $enum_name {
+                ($vval << 1)
+                [
+                    $($out)*
+                    $(#[$mout])*
+                    $(#[$($mtail)+])*
+                    $selinux_name $vname = $vval,
+                ]
+                [$($tail)*]
+            }
+        }
+    };
+
+    (
+        @extract_attr
+        $(#[$enum_meta:meta])*
+        $enum_vis:vis enum $enum_name:ident {
+            $next_val:tt
+            [$($out:tt)*]
+            [
+                [$(#[$mout:meta])*]
+                [
+                    #[selinux(name = $selinux_name:ident)]
+                    $(#[$($mtail:tt)+])*
+                ]
+                $vname:ident,
+                $($tail:tt)*
+            ]
+        }
+    ) => {
+        implement_permission!{
+            @extract_attr
+            $(#[$enum_meta])*
+            $enum_vis enum $enum_name {
+                ($next_val << 1)
+                [
+                    $($out)*
+                    $(#[$mout])*
+                    $(#[$($mtail)+])*
+                    $selinux_name $vname = $next_val,
+                ]
+                [$($tail)*]
+            }
+        }
+    };
+
+
+    (
+        @extract_attr
+        $(#[$enum_meta:meta])*
+        $enum_vis:vis enum $enum_name:ident {
+            $next_val:tt
+            [$($out:tt)*]
+            [
+                [$(#[$mout:meta])*]
+                [
+                    #[$front:meta]
+                    $(#[$($mtail:tt)+])*
+                ]
+                $vname:ident$( = $vval:tt)?,
+                $($tail:tt)*
+            ]
+        }
+    ) => {
+        implement_permission!{
+            @extract_attr
+            $(#[$enum_meta])*
+            $enum_vis enum $enum_name {
+                $next_val
+                [$($out)*]
+                [
+                    [
+                        $(#[$mout])*
+                        #[$front]
+                    ]
+                    [$(#[$($mtail)+])*]
+                    $vname$( = $vval)?,
+                    $($tail)*
+                ]
+            }
+        }
+    };
+
+    (
+        @extract_attr
+        $(#[$enum_meta:meta])*
+        $enum_vis:vis enum $enum_name:ident {
+            $next_val:tt
+            [$($out:tt)*]
+            []
+        }
+    ) => {
+        implement_permission!{
+            @spill
+            $(#[$enum_meta])*
+            $enum_vis enum $enum_name {
+                $($out)*
+            }
+        }
+    };
+
+    (
+        @spill
+        $(#[$enum_meta:meta])*
+        $enum_vis:vis enum $enum_name:ident {
+            $(
+                $(#[$emeta:meta])*
+                $selinux_name:ident $vname:ident = $vval:tt,
+            )*
+        }
+    ) => {
+        $(#[$enum_meta])*
+        $enum_vis enum $enum_name {
+            /// The default variant of an enum.
+            None = 0,
+            $(
+                $(#[$emeta])*
+                $vname = $vval,
+            )*
+        }
+
+        impl From<i32> for $enum_name {
+            #[allow(non_upper_case_globals)]
+            fn from (p: i32) -> Self {
+                // Creating constants forces the compiler to evaluate the value expressions
+                // so that they can be used in the match statement below.
+                $(const $vname: i32 = $vval;)*
+                match p {
+                    0 => Self::None,
+                    $($vname => Self::$vname,)*
+                    _ => Self::None,
+                }
+            }
+        }
+
+        impl From<$enum_name> for i32 {
+            fn from(p: $enum_name) -> i32 {
+                p as i32
+            }
+        }
+
+        impl $enum_name {
+
+            /// Returns a string representation of the permission as required by
+            /// `selinux::check_access`.
+            pub fn to_selinux(self) -> &'static str {
+                match self {
+                    Self::None => &"none",
+                    $(Self::$vname => stringify!($selinux_name),)*
+                }
+            }
+
+            /// Creates an instance representing a permission with the same name.
+            pub const fn none() -> Self { Self::None }
+            $(
+                /// Creates an instance representing a permission with the same name.
+                pub const fn $selinux_name() -> Self { Self::$vname }
+            )*
+        }
+    };
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;