{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveAnyClass #-}

module Raft.Persistent where

import Protolude
import qualified Data.Serialize as S

import Raft.Types

-- | Provides an interface to read and write the persistent
-- state to disk.
class Monad m => RaftPersist m where
  type RaftPersistError m
  initializePersistentState
    :: Exception (RaftPersistError m)
    => m (Either (RaftPersistError m) ())
  readPersistentState
    :: Exception (RaftPersistError m)
    => m (Either (RaftPersistError m) PersistentState)
  writePersistentState
    :: Exception (RaftPersistError m)
    => PersistentState -> m (Either (RaftPersistError m) ())

-- | Persistent state that all Raft nodes maintain, regardless of node state.
data PersistentState = PersistentState
  { currentTerm :: !Term
    -- ^ Last term server has seen
  , votedFor :: !(Maybe NodeId)
    -- ^ Candidate id that received vote in current term
  } deriving (Show, Eq, Generic, S.Serialize)

-- | A node initiates its persistent state with term 0 and with its vote blank
initPersistentState :: PersistentState
initPersistentState = PersistentState
  { currentTerm = term0
  , votedFor = Nothing
  }