module Ecluse.Internal (
ProcessOutcome (..),
superviseProcess,
exitCodeFor,
exitReasonFor,
) where
import Control.Exception (AsyncException (ThreadKilled, UserInterrupt), SomeAsyncException)
import Control.Exception qualified as Exception
import System.Exit (ExitCode (ExitFailure, ExitSuccess))
import Ecluse.Boot (BootAborted (BootAborted))
import Ecluse.Core.Text (displayExceptionT)
data ProcessOutcome
=
ShutdownRequested
|
ServiceExited Text
|
BootFault Text
|
RunCancelled
deriving stock (ProcessOutcome -> ProcessOutcome -> Bool
(ProcessOutcome -> ProcessOutcome -> Bool)
-> (ProcessOutcome -> ProcessOutcome -> Bool) -> Eq ProcessOutcome
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: ProcessOutcome -> ProcessOutcome -> Bool
== :: ProcessOutcome -> ProcessOutcome -> Bool
$c/= :: ProcessOutcome -> ProcessOutcome -> Bool
/= :: ProcessOutcome -> ProcessOutcome -> Bool
Eq, Int -> ProcessOutcome -> ShowS
[ProcessOutcome] -> ShowS
ProcessOutcome -> String
(Int -> ProcessOutcome -> ShowS)
-> (ProcessOutcome -> String)
-> ([ProcessOutcome] -> ShowS)
-> Show ProcessOutcome
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ProcessOutcome -> ShowS
showsPrec :: Int -> ProcessOutcome -> ShowS
$cshow :: ProcessOutcome -> String
show :: ProcessOutcome -> String
$cshowList :: [ProcessOutcome] -> ShowS
showList :: [ProcessOutcome] -> ShowS
Show)
superviseProcess :: IO ProcessOutcome -> IO ProcessOutcome
superviseProcess :: IO ProcessOutcome -> IO ProcessOutcome
superviseProcess IO ProcessOutcome
service =
IO ProcessOutcome -> IO (Either SomeException ProcessOutcome)
forall e a. Exception e => IO a -> IO (Either e a)
Exception.try IO ProcessOutcome
service IO (Either SomeException ProcessOutcome)
-> (Either SomeException ProcessOutcome -> IO ProcessOutcome)
-> IO ProcessOutcome
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Right ProcessOutcome
outcome -> ProcessOutcome -> IO ProcessOutcome
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ProcessOutcome
outcome
Left SomeException
err
| Just (BootAborted Text
rendered) <- SomeException -> Maybe BootAborted
forall e. Exception e => SomeException -> Maybe e
fromException SomeException
err -> ProcessOutcome -> IO ProcessOutcome
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Text -> ProcessOutcome
BootFault Text
rendered)
| Just (ExitCode
code :: ExitCode) <- SomeException -> Maybe ExitCode
forall e. Exception e => SomeException -> Maybe e
fromException SomeException
err -> ExitCode -> IO ProcessOutcome
forall e a. (HasCallStack, Exception e) => e -> IO a
Exception.throwIO ExitCode
code
| Just (AsyncException
killed :: AsyncException) <- SomeException -> Maybe AsyncException
forall e. Exception e => SomeException -> Maybe e
fromException SomeException
err ->
ProcessOutcome -> IO ProcessOutcome
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (ProcessOutcome -> IO ProcessOutcome)
-> ProcessOutcome -> IO ProcessOutcome
forall a b. (a -> b) -> a -> b
$ case AsyncException
killed of
AsyncException
ThreadKilled -> ProcessOutcome
RunCancelled
AsyncException
UserInterrupt -> ProcessOutcome
RunCancelled
AsyncException
other -> Text -> ProcessOutcome
ServiceExited (AsyncException -> Text
forall e. Exception e => e -> Text
displayExceptionT AsyncException
other)
| Just (SomeAsyncException
_ :: SomeAsyncException) <- SomeException -> Maybe SomeAsyncException
forall e. Exception e => SomeException -> Maybe e
fromException SomeException
err -> SomeException -> IO ProcessOutcome
forall e a. (HasCallStack, Exception e) => e -> IO a
Exception.throwIO SomeException
err
| Bool
otherwise -> ProcessOutcome -> IO ProcessOutcome
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Text -> ProcessOutcome
ServiceExited (SomeException -> Text
forall e. Exception e => e -> Text
displayExceptionT SomeException
err))
data ProcessExit
= ExitedCleanly
| ExitedWith ExitCode Text
processExitFor :: ProcessOutcome -> ProcessExit
processExitFor :: ProcessOutcome -> ProcessExit
processExitFor = \case
ProcessOutcome
ShutdownRequested -> ProcessExit
ExitedCleanly
ServiceExited Text
detail -> ExitCode -> Text -> ProcessExit
ExitedWith (Int -> ExitCode
ExitFailure Int
1) (Text
"ecluse: service exited: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
detail)
BootFault Text
rendered -> ExitCode -> Text -> ProcessExit
ExitedWith (Int -> ExitCode
ExitFailure Int
2) Text
rendered
ProcessOutcome
RunCancelled -> ExitCode -> Text -> ProcessExit
ExitedWith (Int -> ExitCode
ExitFailure Int
3) Text
"ecluse: run cancelled"
exitCodeFor :: ProcessOutcome -> ExitCode
exitCodeFor :: ProcessOutcome -> ExitCode
exitCodeFor ProcessOutcome
outcome = case ProcessOutcome -> ProcessExit
processExitFor ProcessOutcome
outcome of
ProcessExit
ExitedCleanly -> ExitCode
ExitSuccess
ExitedWith ExitCode
code Text
_ -> ExitCode
code
exitReasonFor :: ProcessOutcome -> Maybe Text
exitReasonFor :: ProcessOutcome -> Maybe Text
exitReasonFor ProcessOutcome
outcome = case ProcessOutcome -> ProcessExit
processExitFor ProcessOutcome
outcome of
ProcessExit
ExitedCleanly -> Maybe Text
forall a. Maybe a
Nothing
ExitedWith ExitCode
_ Text
reason -> Text -> Maybe Text
forall a. a -> Maybe a
Just Text
reason