From 374584c5fc21ae3ff7cf1c0bc5a1fe59d5d45f86 Mon Sep 17 00:00:00 2001
From: Georgy Moshkin <gmoshkin@picodata.io>
Date: Thu, 8 Sep 2022 18:09:06 +0300
Subject: [PATCH] refactor: extract define_str_enum macro

---
 src/traft/event.rs | 63 +++++++++++++---------------------------------
 src/util.rs        | 62 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 79 insertions(+), 46 deletions(-)

diff --git a/src/traft/event.rs b/src/traft/event.rs
index fed643694a..c56f68a379 100644
--- a/src/traft/event.rs
+++ b/src/traft/event.rs
@@ -9,6 +9,7 @@ use ::tarantool::fiber::{mutex::MutexGuard, Cond, Mutex};
 use ::tarantool::proc;
 use ::tarantool::unwrap_or;
 
+use crate::define_str_enum;
 use crate::tlog;
 use crate::traft::error::Error;
 use crate::unwrap_ok_or;
@@ -17,54 +18,24 @@ use thiserror::Error;
 pub type BoxResult<T> = std::result::Result<T, Box<dyn std::error::Error>>;
 
 #[derive(Error, Debug)]
-#[error("unknown event")]
-pub struct EventFromStrError;
-
-macro_rules! define_events {
-    ($($event:tt, $str:literal;)+) => {
-        ////////////////////////////////////////////////////////////////////////
-        /// An enumeration of builtin events
-        #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord)]
-        pub enum Event {
-            $( $event, )+
-        }
-
-        impl Event {
-            pub const fn as_str(&self) -> &str {
-                match self {
-                    $( Self::$event => $str, )+
-                }
-            }
-        }
-
-        impl std::fmt::Display for Event {
-            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
-                f.write_str(self.as_str())
-            }
-        }
-
-        impl FromStr for Event {
-            type Err = EventFromStrError;
-
-            fn from_str(s: &str) -> Result<Self, Self::Err> {
-                match s {
-                    $( $str => Ok(Self::$event), )+
-                    _ => Err(EventFromStrError),
-                }
-            }
-        }
+#[error("unknown event {0}")]
+pub struct EventFromStrError(pub String);
+
+define_str_enum! {
+    ////////////////////////////////////////////////////////////////////////////
+    /// An enumeration of builtin events
+    pub enum Event {
+        Demoted = "raft.demoted",
+        JointStateEnter = "raft.joint-state-enter",
+        JointStateLeave = "raft.joint-state-leave",
+        JointStateDrop = "raft.joint-state-drop",
+        StatusChanged = "raft.status-changed",
+        TopologyChanged = "raft.topology-changed",
+        RaftLoopNeeded = "raft.loop-needed",
+        RaftEntryApplied = "raft.entry-applied",
     }
-}
 
-define_events! {
-    Demoted, "raft.demoted";
-    JointStateEnter, "raft.joint-state-enter";
-    JointStateLeave, "raft.joint-state-leave";
-    JointStateDrop, "raft.joint-state-drop";
-    StatusChanged, "raft.status-changed";
-    TopologyChanged, "raft.topology-changed";
-    RaftLoopNeeded, "raft.loop-needed";
-    RaftEntryApplied, "raft.entry-applied";
+    FromStr::Err = EventFromStrError;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/src/util.rs b/src/util.rs
index abb407246c..4bda629fcc 100644
--- a/src/util.rs
+++ b/src/util.rs
@@ -33,6 +33,68 @@ macro_rules! stringify_debug {
     }};
 }
 
+#[macro_export]
+macro_rules! define_str_enum {
+    (
+        $(#[$meta:meta])*
+        pub enum $enum:ident { $($space:tt = $str:literal,)+ }
+        FromStr::Err = $err:ident;
+    ) => {
+        $(#[$meta])*
+        #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, PartialOrd, Ord)]
+        pub enum $enum {
+            $( #[doc = $str] $space, )+
+        }
+
+        impl $enum {
+            pub const fn as_str(&self) -> &str {
+                match self {
+                    $( Self::$space => $str, )+
+                }
+            }
+        }
+
+        impl std::str::FromStr for $enum {
+            type Err = $err;
+
+            fn from_str(s: &str) -> Result<Self, Self::Err> {
+                match s {
+                    $( $str => Ok(Self::$space), )+
+                    _ => Err($err(s.into())),
+                }
+            }
+        }
+
+        impl std::fmt::Display for $enum {
+            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+                f.write_str(self.as_str())
+            }
+        }
+
+        impl serde::Serialize for $enum {
+            #[inline]
+            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+            where
+                S: serde::Serializer,
+            {
+                serializer.serialize_str(self.as_str())
+            }
+        }
+
+        impl<'de> serde::Deserialize<'de> for $enum {
+            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+            where
+                D: serde::Deserializer<'de>,
+            {
+                use serde::de::Error;
+                let tmp = <&str>::deserialize(deserializer)?;
+                let res = tmp.parse().map_err(|e| D::Error::custom(e))?;
+                Ok(res)
+            }
+        }
+    }
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 /// A wrapper around `String` that garantees the string is uppercase by
 /// converting it to uppercase (if needed) on construction.
-- 
GitLab