From 27098d15f6928396311930d61af84489e5a2d99f Mon Sep 17 00:00:00 2001
From: Georgy Moshkin <gmoshkin@picodata.io>
Date: Wed, 21 Sep 2022 18:21:46 +0300
Subject: [PATCH] refactor(storage): peer or peer_field by generic impl PeerId

---
 src/traft/error.rs   |  5 +++-
 src/traft/storage.rs | 54 ++++++++++++++++++++++++++++++++++++--------
 2 files changed, 49 insertions(+), 10 deletions(-)

diff --git a/src/traft/error.rs b/src/traft/error.rs
index 4dbf27263d..7459e495b6 100644
--- a/src/traft/error.rs
+++ b/src/traft/error.rs
@@ -1,3 +1,4 @@
+use crate::traft::InstanceId;
 use crate::traft::RaftId;
 use ::tarantool::tlua::LuaError;
 use raft::StorageError;
@@ -28,7 +29,9 @@ pub enum Error {
     Tarantool(#[from] ::tarantool::error::Error),
     #[error("peer with id {0} not found")]
     NoPeerWithRaftId(RaftId),
-    #[error("other error")]
+    #[error(r#"peer with id "{0}" not found"#)]
+    NoPeerWithInstanceId(InstanceId),
+    #[error("other error: {0}")]
     Other(Box<dyn std::error::Error>),
 }
 
diff --git a/src/traft/storage.rs b/src/traft/storage.rs
index cfb2dd54c0..6c3b46b455 100644
--- a/src/traft/storage.rs
+++ b/src/traft/storage.rs
@@ -297,19 +297,25 @@ impl Peers {
 
     /// Find a peer by `raft_id` and return a single field specified by `F`
     /// (see `PeerFieldDef` & `peer_field` module).
-    #[inline]
-    pub fn field_by_raft_id<F>(&self, raft_id: RaftId) -> Result<F::Type, TraftError>
+    #[inline(always)]
+    #[allow(dead_code)]
+    pub fn peer<F>(&self, id: &impl PeerId) -> Result<F::Type, TraftError>
     where
         F: PeerFieldDef,
     {
-        if raft_id == INVALID_ID {
-            unreachable!("peer_by_raft_id called with invalid id ({})", INVALID_ID);
-        }
+        let res = id.find_in(self)?.decode().expect("failed to decode peer");
+        Ok(res)
+    }
 
-        let res = self
-            .index_raft_id
-            .get(&[raft_id])?
-            .ok_or(TraftError::NoPeerWithRaftId(raft_id))?
+    /// Find a peer by `id` (see `PeerId`) and return a single field
+    /// specified by `F` (see `PeerFieldDef` & `peer_field` module).
+    #[inline(always)]
+    pub fn peer_field<F>(&self, id: &impl PeerId) -> Result<F::Type, TraftError>
+    where
+        F: PeerFieldDef,
+    {
+        let res = id
+            .find_in(self)?
             .get(F::NAME)
             .expect("peer fields are not nullable");
         Ok(res)
@@ -477,6 +483,36 @@ pub trait PeerFieldDef {
     const TYPE: tarantool::space::FieldType;
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// PeerId
+////////////////////////////////////////////////////////////////////////////////
+
+/// Types implementing this trait can be used to identify a `Peer` when
+/// accessing storage.
+pub trait PeerId: serde::Serialize {
+    fn find_in(&self, peers: &Peers) -> Result<Tuple, TraftError>;
+}
+
+impl PeerId for RaftId {
+    #[inline(always)]
+    fn find_in(&self, peers: &Peers) -> Result<Tuple, TraftError> {
+        peers
+            .index_raft_id
+            .get(&[self])?
+            .ok_or(TraftError::NoPeerWithRaftId(*self))
+    }
+}
+
+impl PeerId for traft::InstanceId {
+    #[inline(always)]
+    fn find_in(&self, peers: &Peers) -> Result<Tuple, TraftError> {
+        peers
+            .index_instance_id
+            .get(&[self])?
+            .ok_or_else(|| TraftError::NoPeerWithInstanceId(self.clone()))
+    }
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // tests
 ////////////////////////////////////////////////////////////////////////////////
-- 
GitLab