packages feed

keiro-core-0.6.0.0: src/Keiro/Snapshot/Policy.hs

-- | Evaluation of a stream's 'SnapshotPolicy'.
--
-- This is the single decision procedure command handling consults after an
-- append to decide whether to persist a snapshot of the folded state. It
-- keeps the 'SnapshotPolicy' constructors purely declarative — the meaning
-- of each constructor lives here.
module Keiro.Snapshot.Policy
  ( shouldSnapshot,
    shouldSnapshotSpan,
  )
where

import Keiro.EventStream (SnapshotPolicy (..), Terminality (..))
import Keiro.Prelude
import Kiroku.Store.Types (StreamVersion (..))
import Prelude qualified

-- | Decide whether to snapshot given a policy, a terminality flag, the
-- folded state, and the post-append stream version.
--
-- * 'Never' is always 'False'.
-- * 'Every' @n@ is 'True' when the version is a positive multiple of @n@
--   (a non-positive interval never fires).
-- * 'OnTerminal' mirrors the @terminal@ flag — snapshot exactly when the
--   machine has reached a final state.
-- * 'Custom' defers to the caller-supplied predicate over state and version.
shouldSnapshot :: SnapshotPolicy state -> Terminality -> state -> StreamVersion -> Bool
shouldSnapshot Never _ _ _ = False
shouldSnapshot (Every interval) _ _ (StreamVersion version)
  | interval <= 0 = False
  | otherwise = version > 0 Prelude.&& version `Prelude.mod` Prelude.fromIntegral interval == 0
shouldSnapshot OnTerminal terminality _ _ = terminality == Terminal
shouldSnapshot (Custom decide) terminality state version = decide terminality state version

-- | Like 'shouldSnapshot', but evaluated over the half-open stream-version
-- span @(preVersion, postVersion]@ that one append covered.
--
-- For 'Every' @n@, this fires when any positive multiple of @n@ lies inside
-- the span, so a batch append that jumps over a boundary still snapshots at
-- the post-append version. The other policies ignore the span and behave like
-- 'shouldSnapshot'.
shouldSnapshotSpan ::
  SnapshotPolicy state ->
  Terminality ->
  state ->
  StreamVersion ->
  StreamVersion ->
  Bool
shouldSnapshotSpan Never _ _ _ _ = False
shouldSnapshotSpan (Every interval) _ _ (StreamVersion preVersion) (StreamVersion postVersion)
  | interval <= 0 = False
  | postVersion <= 0 = False
  | otherwise = postVersion `Prelude.div` n > preVersion `Prelude.div` n
  where
    n = Prelude.fromIntegral interval
shouldSnapshotSpan OnTerminal terminality _ _ _ = terminality == Terminal
shouldSnapshotSpan (Custom decide) terminality state _ postVersion = decide terminality state postVersion