{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RequiredTypeArguments #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
module Hindsight.Projection.Matching (
ProjectionHandler,
ProjectionHandlers (..),
SomeProjectionHandler (..),
extractMatchingHandlers,
handlersForEventName,
)
where
import Data.Proxy (Proxy (..))
import Data.Text (Text)
import Data.Typeable (Typeable, eqT, (:~:) (Refl))
import GHC.TypeLits (Symbol)
import Hasql.Transaction (Transaction)
import Hindsight.Events (Event, getEventName)
import Hindsight.Store (EventEnvelope)
type ProjectionHandler event backend =
EventEnvelope event backend -> Transaction ()
data ProjectionHandlers (ts :: [Symbol]) backend where
(:->) ::
(Event event, Typeable (ProjectionHandler event backend)) =>
(Proxy event, ProjectionHandler event backend) ->
ProjectionHandlers ts backend ->
ProjectionHandlers (event ': ts) backend
ProjectionEnd :: ProjectionHandlers '[] backend
infixr 5 :->
extractMatchingHandlers ::
forall event ts backend.
(Event event) =>
ProjectionHandlers ts backend ->
Proxy event ->
[ProjectionHandler event backend]
extractMatchingHandlers :: forall (event :: Symbol) (ts :: [Symbol]) backend.
Event event =>
ProjectionHandlers ts backend
-> Proxy event -> [ProjectionHandler event backend]
extractMatchingHandlers ProjectionHandlers ts backend
handlers Proxy event
eventProxy = ProjectionHandlers ts backend -> [ProjectionHandler event backend]
forall (ts' :: [Symbol]).
ProjectionHandlers ts' backend -> [ProjectionHandler event backend]
matchHandlers ProjectionHandlers ts backend
handlers
where
eventName :: Text
eventName = Proxy event -> Text
forall (event :: Symbol). KnownSymbol event => Proxy event -> Text
getEventName Proxy event
eventProxy
matchHandlers :: ProjectionHandlers ts' backend -> [ProjectionHandler event backend]
matchHandlers :: forall (ts' :: [Symbol]).
ProjectionHandlers ts' backend -> [ProjectionHandler event backend]
matchHandlers ProjectionHandlers ts' backend
ProjectionEnd = []
matchHandlers ((Proxy event
handlerProxy :: Proxy handlerEvent, ProjectionHandler event backend
handler) :-> ProjectionHandlers ts backend
rest) =
let handlerName :: Text
handlerName = Proxy event -> Text
forall (event :: Symbol). KnownSymbol event => Proxy event -> Text
getEventName Proxy event
handlerProxy
in if Text
eventName Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
handlerName
then
case forall {k} (a :: k) (b :: k).
(Typeable a, Typeable b) =>
Maybe (a :~: b)
forall (a :: Symbol) (b :: Symbol).
(Typeable a, Typeable b) =>
Maybe (a :~: b)
eqT @handlerEvent @event of
Just event :~: event
Refl -> ProjectionHandler event backend
ProjectionHandler event backend
handler ProjectionHandler event backend
-> [ProjectionHandler event backend]
-> [ProjectionHandler event backend]
forall a. a -> [a] -> [a]
: ProjectionHandlers ts backend -> [ProjectionHandler event backend]
forall (ts' :: [Symbol]).
ProjectionHandlers ts' backend -> [ProjectionHandler event backend]
matchHandlers ProjectionHandlers ts backend
rest
Maybe (event :~: event)
Nothing -> ProjectionHandlers ts backend -> [ProjectionHandler event backend]
forall (ts' :: [Symbol]).
ProjectionHandlers ts' backend -> [ProjectionHandler event backend]
matchHandlers ProjectionHandlers ts backend
rest
else
ProjectionHandlers ts backend -> [ProjectionHandler event backend]
forall (ts' :: [Symbol]).
ProjectionHandlers ts' backend -> [ProjectionHandler event backend]
matchHandlers ProjectionHandlers ts backend
rest
data SomeProjectionHandler backend
= forall event.
(Event event) =>
SomeProjectionHandler (Proxy event) (ProjectionHandler event backend)
handlersForEventName ::
Text ->
ProjectionHandlers ts backend ->
[SomeProjectionHandler backend]
handlersForEventName :: forall (ts :: [Symbol]) backend.
Text
-> ProjectionHandlers ts backend -> [SomeProjectionHandler backend]
handlersForEventName Text
targetEventName = ProjectionHandlers ts backend -> [SomeProjectionHandler backend]
forall (ts' :: [Symbol]) backend.
ProjectionHandlers ts' backend -> [SomeProjectionHandler backend]
go
where
go :: ProjectionHandlers ts' backend -> [SomeProjectionHandler backend]
go :: forall (ts' :: [Symbol]) backend.
ProjectionHandlers ts' backend -> [SomeProjectionHandler backend]
go ProjectionHandlers ts' backend
ProjectionEnd = []
go ((Proxy event
eventProxy, ProjectionHandler event backend
handler) :-> ProjectionHandlers ts backend
rest) =
let handlerEventName :: Text
handlerEventName = Proxy event -> Text
forall (event :: Symbol). KnownSymbol event => Proxy event -> Text
getEventName Proxy event
eventProxy
in if Text
targetEventName Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
handlerEventName
then Proxy event
-> ProjectionHandler event backend -> SomeProjectionHandler backend
forall backend (event :: Symbol).
Event event =>
Proxy event
-> ProjectionHandler event backend -> SomeProjectionHandler backend
SomeProjectionHandler Proxy event
eventProxy ProjectionHandler event backend
handler SomeProjectionHandler backend
-> [SomeProjectionHandler backend]
-> [SomeProjectionHandler backend]
forall a. a -> [a] -> [a]
: ProjectionHandlers ts backend -> [SomeProjectionHandler backend]
forall (ts' :: [Symbol]) backend.
ProjectionHandlers ts' backend -> [SomeProjectionHandler backend]
go ProjectionHandlers ts backend
rest
else ProjectionHandlers ts backend -> [SomeProjectionHandler backend]
forall (ts' :: [Symbol]) backend.
ProjectionHandlers ts' backend -> [SomeProjectionHandler backend]
go ProjectionHandlers ts backend
rest