mortred-0.0.1: src/Mortred/Session.hs
module Mortred.Session where
import Mortred.Selenium
import Mortred.Types
import Mortred.Utilities
import Mortred.Xvfb
import RIO
import System.Process.Typed (stopProcess)
-- | Defines how to handle potential starting up of Xvfb & Selenium.
data SessionMode
= -- | Start Xvfb and Selenium.
SessionOnDemand
| -- | Don't start Xvfb and Selenium.
SessionAlreadyStarted SeleniumPort
deriving (Eq, Show)
-- | The result of successfully starting a session.
data SessionStartResult
= -- | Xvfb and Selenium were started and here is the Selenium process.
StartedOnDemand !SeleniumProcess
| -- | The session was premade and here is the port to connect to.
PremadeSession !SeleniumPort
deriving (Show)
data SessionStartError
= XvfbSessionError XvfbStartError
| SeleniumSessionError SeleniumStartError
deriving (Show)
instance Exception SessionStartError
-- | Tries to start a session, returning 'Left' with a 'SessionStartError' if it fails or 'Right'
-- with a 'SessionStartResult' if it succeeds. Errors that can be expected to be caught here are
-- process read errors (for `google-chrome` & `chromedriver`), display allocation errors (for Xvfb),
-- and Selenium start errors (port allocation, unable to find selenium file) as well as HTTP errors
-- for automatically downloading `chromedriver`.
tryStartSession ::
(MonadUnliftIO m, MonadThrow m, PrimMonad m, MonadFail m) =>
SessionMode ->
SeleniumPath ->
m (Either SessionStartError SessionStartResult)
tryStartSession sessionMode =
startSession sessionMode >>> try
-- | Starts a session, throwing a 'SessionStartError' on failure. Use 'tryStartSession' in order to
-- automatically capture this error.
startSession ::
(MonadThrow m, MonadUnliftIO m, PrimMonad m, MonadFail m) =>
SessionMode ->
SeleniumPath ->
m SessionStartResult
startSession SessionOnDemand seleniumPath = do
xvfbProcess@XvfbProcess {process} <- mapExceptionM XvfbSessionError startXvfb
(StartedOnDemand <$> startSelenium xvfbProcess seleniumPath)
`catch` ( \(e :: SeleniumStartError) -> do
stopProcess process
throwM $ SeleniumSessionError e
)
startSession (SessionAlreadyStarted seleniumPort) _seleniumPath = do
pure $ PremadeSession seleniumPort
stopSession :: SeleniumProcess -> IO ()
stopSession SeleniumProcess {xvfbProcess = XvfbProcess {process = xvfbProcess}, process} = do
stopProcess process
stopProcess xvfbProcess