{-# LANGUAGE UndecidableInstances #-}

-- | This module defines the types and functions for creating 'EventSource' and
-- 'EventSink' instances and is intended to be used as an extension point.
--
-- A single 'EventSource' and zero or more 'EventSink' handles are used by the
-- main 'HydraNode' handle to load and send out events.
--
-- See 'Hydra.Events.FileBased' for an example implementation and
-- 'Hydra.Events.FileBasedSpec' for the corresponding test suite.
--
-- Custom implementations should be located under Hydra.Events to avoid
-- conflicts.
module Hydra.Events where

import Hydra.Prelude

import Conduit (ConduitT, MonadUnliftIO, ResourceT, runResourceT, sourceToList)

type EventId = Word64

class HasEventId a where
  getEventId :: a -> EventId

instance HasEventId Word64 where
  getEventId :: Word64 -> Word64
getEventId = Word64 -> Word64
forall a. a -> a
id

newtype EventSource e m = EventSource
  { forall e (m :: * -> *).
EventSource e m -> HasEventId e => ConduitT () e (ResourceT m) ()
sourceEvents :: HasEventId e => ConduitT () e (ResourceT m) ()
  -- ^ Stream all events from the event source.
  }

-- | Retrieve all events from the event source as a list.
getEvents :: (HasEventId e, MonadUnliftIO m) => EventSource e m -> m [e]
getEvents :: forall e (m :: * -> *).
(HasEventId e, MonadUnliftIO m) =>
EventSource e m -> m [e]
getEvents EventSource{HasEventId e => ConduitT () e (ResourceT m) ()
$sel:sourceEvents:EventSource :: forall e (m :: * -> *).
EventSource e m -> HasEventId e => ConduitT () e (ResourceT m) ()
sourceEvents :: HasEventId e => ConduitT () e (ResourceT m) ()
sourceEvents} = ResourceT m [e] -> m [e]
forall (m :: * -> *) a. MonadUnliftIO m => ResourceT m a -> m a
runResourceT (ResourceT m [e] -> m [e]) -> ResourceT m [e] -> m [e]
forall a b. (a -> b) -> a -> b
$ ConduitT () e (ResourceT m) () -> ResourceT m [e]
forall (m :: * -> *) a. Monad m => ConduitT () a m () -> m [a]
sourceToList ConduitT () e (ResourceT m) ()
HasEventId e => ConduitT () e (ResourceT m) ()
sourceEvents

newtype EventSink e m = EventSink
  { forall e (m :: * -> *). EventSink e m -> HasEventId e => e -> m ()
putEvent :: HasEventId e => e -> m ()
  -- ^ Send a single event to the event sink.
  }

-- | Put a list of events to a list of event sinks in a round-robin fashion.
putEventsToSinks :: (Monad m, HasEventId e) => [EventSink e m] -> [e] -> m ()
putEventsToSinks :: forall (m :: * -> *) e.
(Monad m, HasEventId e) =>
[EventSink e m] -> [e] -> m ()
putEventsToSinks [EventSink e m]
sinks [e]
events =
  [e] -> (e -> m ()) -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ [e]
events ((e -> m ()) -> m ()) -> (e -> m ()) -> m ()
forall a b. (a -> b) -> a -> b
$ \e
event ->
    [EventSink e m] -> (EventSink e m -> m ()) -> m ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ [EventSink e m]
sinks ((EventSink e m -> m ()) -> m ())
-> (EventSink e m -> m ()) -> m ()
forall a b. (a -> b) -> a -> b
$ \EventSink e m
sink ->
      EventSink e m -> HasEventId e => e -> m ()
forall e (m :: * -> *). EventSink e m -> HasEventId e => e -> m ()
putEvent EventSink e m
sink e
event