summaryrefslogtreecommitdiff
path: root/hsm-core/Hsm/Core/Fsm.hs
diff options
context:
space:
mode:
Diffstat (limited to 'hsm-core/Hsm/Core/Fsm.hs')
-rw-r--r--hsm-core/Hsm/Core/Fsm.hs43
1 files changed, 43 insertions, 0 deletions
diff --git a/hsm-core/Hsm/Core/Fsm.hs b/hsm-core/Hsm/Core/Fsm.hs
new file mode 100644
index 0000000..caa2e7e
--- /dev/null
+++ b/hsm-core/Hsm/Core/Fsm.hs
@@ -0,0 +1,43 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+-- Module : Hsm.Core.Fsm
+-- Maintainer : contact@pauloliver.dev
+module Hsm.Core.Fsm
+ ( Method (Method)
+ , FsmC
+ , fsm
+ )
+where
+
+import Control.Exception.Safe (Exception, catch, displayException)
+import Data.Text (Text, pack)
+import Effectful ((:>))
+import Effectful.Log (Log, localDomain, logAttention_, logTrace_)
+import Effectful.Reader.Static (Reader)
+import Effectful.State.Static.Local (State)
+import GHC.Records (HasField)
+import Hsm.Core.Pipes (Pipe)
+
+data Method a b e s x es = Method Text (Pipe a b e s es (Method a b e s x es))
+
+type FsmC e s x es = (HasField "name" e Text, Exception x, Log :> es, Reader e :> es, State s :> es)
+
+-- Builds an FSM with an initial and a default method. Because both @Proxy@
+-- and @Eff@ are instances of @MonadCatch@ and @MonadThrow@, exceptions may be
+-- thrown from within state methods. The FSM transitions to its default state
+-- if an exception is thrown.
+fsm :: forall a b e s x es. FsmC e s x es => Method a b e s x es -> Method a b e s x es -> Pipe a b e s es ()
+fsm actionInit actionDefault = localDomain "fsm" $ run actionInit
+ where
+ run :: Method a b e s x es -> Pipe a b e s es ()
+ run (Method name action) = do
+ logTrace_ $ "Entering state " <> name
+ next <- localDomain name action `catch` handle
+ logTrace_ $ "Exiting state " <> name
+ run next
+ where
+ handle :: x -> Pipe a b e s es (Method a b e s x es)
+ handle exception = do
+ logAttention_ $ "Exception caught while on state " <> name
+ logAttention_ $ pack $ displayException exception
+ return actionDefault