packages feed

seihou-core-0.4.0.0: src/Seihou/Effect/ConsolePure.hs

module Seihou.Effect.ConsolePure
  ( runConsolePure,
    runConsolePureNonInteractive,
    ConsoleState (..),
    emptyConsoleState,
  )
where

import Effectful.State.Static.Local (State, get, modify, runState)
import Seihou.Effect.Console (Console (..))
import Seihou.Prelude
import Prelude hiding (getLine)

-- | State for the pure Console interpreter.
data ConsoleState = ConsoleState
  { consoleInputs :: [Text],
    consoleOutputs :: [Text],
    consoleErrors :: [Text]
  }
  deriving stock (Eq, Show)

-- | Empty console state with no inputs or outputs.
emptyConsoleState :: ConsoleState
emptyConsoleState = ConsoleState [] [] []

-- | Pure interpreter for the Console effect (interactive mode).
-- Takes a list of scripted input lines. IsInteractive returns True.
runConsolePure :: [Text] -> Eff (Console : es) a -> Eff es (a, ConsoleState)
runConsolePure inputs = reinterpret (runState (ConsoleState inputs [] [])) handler
  where
    handler :: (State ConsoleState :> es') => EffectHandler Console es'
    handler _ = \case
      PutText msg -> modify @ConsoleState (\s -> s {consoleOutputs = s.consoleOutputs ++ [msg]})
      PutError msg -> modify @ConsoleState (\s -> s {consoleErrors = s.consoleErrors ++ [msg]})
      GetLine -> popInput
      Confirm _prompt -> (`elem` ["y", "yes"]) <$> popInput
      IsInteractive -> pure True

    popInput :: (State ConsoleState :> es') => Eff es' Text
    popInput = do
      s <- get @ConsoleState
      case s.consoleInputs of
        [] -> pure ""
        (x : xs) -> do
          modify @ConsoleState (\st -> st {consoleInputs = xs})
          pure x

-- | Pure interpreter for non-interactive mode. IsInteractive returns False.
runConsolePureNonInteractive :: Eff (Console : es) a -> Eff es (a, ConsoleState)
runConsolePureNonInteractive = reinterpret (runState emptyConsoleState) handler
  where
    handler :: (State ConsoleState :> es') => EffectHandler Console es'
    handler _ = \case
      PutText msg -> modify @ConsoleState (\s -> s {consoleOutputs = s.consoleOutputs ++ [msg]})
      PutError msg -> modify @ConsoleState (\s -> s {consoleErrors = s.consoleErrors ++ [msg]})
      GetLine -> pure ""
      Confirm _prompt -> pure False
      IsInteractive -> pure False