module Eventium.EventBus
  ( synchronousEventBusWrapper,
    storeAndPublishEvents,
  )
where

import Eventium.Store.Class
import Eventium.UUID

-- | This function wraps an event store by sending events to event handlers
-- after running 'storeEvents'. This is useful to quickly wire up event
-- handlers in your application (like read models or process managers), and it
-- is also useful for integration testing along with an in-memory event store.
synchronousEventBusWrapper ::
  (Monad m) =>
  VersionedEventStoreWriter m event ->
  [VersionedEventStoreWriter m event -> UUID -> event -> m ()] ->
  VersionedEventStoreWriter m event
synchronousEventBusWrapper :: forall (m :: * -> *) event.
Monad m =>
VersionedEventStoreWriter m event
-> [VersionedEventStoreWriter m event -> UUID -> event -> m ()]
-> VersionedEventStoreWriter m event
synchronousEventBusWrapper VersionedEventStoreWriter m event
writer [VersionedEventStoreWriter m event -> UUID -> event -> m ()]
handlers = VersionedEventStoreWriter m event
wrappedStore
  where
    -- NB: We need to use recursive let bindings so we can put wrappedStore
    -- inside the event handlers
    handlers' :: [UUID -> event -> m ()]
handlers' = ((VersionedEventStoreWriter m event -> UUID -> event -> m ())
 -> UUID -> event -> m ())
-> [VersionedEventStoreWriter m event -> UUID -> event -> m ()]
-> [UUID -> event -> m ()]
forall a b. (a -> b) -> [a] -> [b]
map ((VersionedEventStoreWriter m event -> UUID -> event -> m ())
-> VersionedEventStoreWriter m event -> UUID -> event -> m ()
forall a b. (a -> b) -> a -> b
$ VersionedEventStoreWriter m event
wrappedStore) [VersionedEventStoreWriter m event -> UUID -> event -> m ()]
handlers
    wrappedStore :: VersionedEventStoreWriter m event
wrappedStore = (UUID
 -> ExpectedPosition EventVersion
 -> [event]
 -> m (Either (EventWriteError EventVersion) EventVersion))
-> VersionedEventStoreWriter m event
forall key position (m :: * -> *) event.
(key
 -> ExpectedPosition position
 -> [event]
 -> m (Either (EventWriteError position) EventVersion))
-> EventStoreWriter key position m event
EventStoreWriter ((UUID
  -> ExpectedPosition EventVersion
  -> [event]
  -> m (Either (EventWriteError EventVersion) EventVersion))
 -> VersionedEventStoreWriter m event)
-> (UUID
    -> ExpectedPosition EventVersion
    -> [event]
    -> m (Either (EventWriteError EventVersion) EventVersion))
-> VersionedEventStoreWriter m event
forall a b. (a -> b) -> a -> b
$ VersionedEventStoreWriter m event
-> [UUID -> event -> m ()]
-> UUID
-> ExpectedPosition EventVersion
-> [event]
-> m (Either (EventWriteError EventVersion) EventVersion)
forall (m :: * -> *) event.
Monad m =>
VersionedEventStoreWriter m event
-> [UUID -> event -> m ()]
-> UUID
-> ExpectedPosition EventVersion
-> [event]
-> m (Either (EventWriteError EventVersion) EventVersion)
storeAndPublishEvents VersionedEventStoreWriter m event
writer [UUID -> event -> m ()]
handlers'

-- | Stores events in the store and then publishes them to the event handlers.
-- This is used in the 'synchronousEventBusWrapper'.
storeAndPublishEvents ::
  (Monad m) =>
  VersionedEventStoreWriter m event ->
  [UUID -> event -> m ()] ->
  UUID ->
  ExpectedPosition EventVersion ->
  [event] ->
  m (Either (EventWriteError EventVersion) EventVersion)
storeAndPublishEvents :: forall (m :: * -> *) event.
Monad m =>
VersionedEventStoreWriter m event
-> [UUID -> event -> m ()]
-> UUID
-> ExpectedPosition EventVersion
-> [event]
-> m (Either (EventWriteError EventVersion) EventVersion)
storeAndPublishEvents VersionedEventStoreWriter m event
store [UUID -> event -> m ()]
handlers UUID
uuid ExpectedPosition EventVersion
expectedVersion [event]
events = do
  Either (EventWriteError EventVersion) EventVersion
result <- VersionedEventStoreWriter m event
-> UUID
-> ExpectedPosition EventVersion
-> [event]
-> m (Either (EventWriteError EventVersion) EventVersion)
forall key position (m :: * -> *) event.
EventStoreWriter key position m event
-> key
-> ExpectedPosition position
-> [event]
-> m (Either (EventWriteError position) EventVersion)
storeEvents VersionedEventStoreWriter m event
store UUID
uuid ExpectedPosition EventVersion
expectedVersion [event]
events
  case Either (EventWriteError EventVersion) EventVersion
result of
    Left EventWriteError EventVersion
err -> Either (EventWriteError EventVersion) EventVersion
-> m (Either (EventWriteError EventVersion) EventVersion)
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either (EventWriteError EventVersion) EventVersion
 -> m (Either (EventWriteError EventVersion) EventVersion))
-> Either (EventWriteError EventVersion) EventVersion
-> m (Either (EventWriteError EventVersion) EventVersion)
forall a b. (a -> b) -> a -> b
$ EventWriteError EventVersion
-> Either (EventWriteError EventVersion) EventVersion
forall a b. a -> Either a b
Left EventWriteError EventVersion
err
    Right EventVersion
vers -> do
      -- NB: If a handler stores events, then its events will be published
      -- before the events of the next handler. That is, we will be storing
      -- events generated by handlers in depth-first order.
      ((UUID -> event -> m ()) -> m ())
-> [UUID -> event -> m ()] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (\UUID -> event -> m ()
handler -> (event -> m ()) -> [event] -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (UUID -> event -> m ()
handler UUID
uuid) [event]
events) [UUID -> event -> m ()]
handlers
      Either (EventWriteError EventVersion) EventVersion
-> m (Either (EventWriteError EventVersion) EventVersion)
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either (EventWriteError EventVersion) EventVersion
 -> m (Either (EventWriteError EventVersion) EventVersion))
-> Either (EventWriteError EventVersion) EventVersion
-> m (Either (EventWriteError EventVersion) EventVersion)
forall a b. (a -> b) -> a -> b
$ EventVersion -> Either (EventWriteError EventVersion) EventVersion
forall a b. b -> Either a b
Right EventVersion
vers