axel-0.0.11: src/Axel/Eff/Process.hs
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
module Axel.Eff.Process where
import Axel.Prelude
import qualified Axel.Utils.Text as T (decodeUtf8Lazy, encodeUtf8Lazy)
import qualified Polysemy as Sem
import Data.Kind (Type)
import Data.Singletons (Sing, SingI, sing)
import Data.Singletons.TH (singletons)
import qualified Data.Text as T
import qualified System.Environment (getArgs)
import System.Exit (ExitCode)
import qualified System.Process.Typed as P
( byteStringInput
, readProcess
, runProcess
, setStdin
, shell
)
$(singletons
[d|
data StreamSpecification = CreateStreams
| InheritStreams
|])
type family StreamsHandler (a :: StreamSpecification) (f :: Type -> Type) :: Type
type instance StreamsHandler 'CreateStreams f =
Text -> f (ExitCode, Text, Text)
type instance StreamsHandler 'InheritStreams f = f ExitCode
type ProcessRunner' (streamSpec :: StreamSpecification) f
= forall streamsHandler. (streamsHandler ~ StreamsHandler streamSpec f) =>
streamsHandler
type ProcessRunnerPrimitive (streamSpec :: StreamSpecification) (f :: Type -> Type)
= FilePath -> [Text] -> ProcessRunner' streamSpec f
type ProcessRunner (streamSpec :: StreamSpecification) (f :: Type -> Type)
= (SingI streamSpec) =>
ProcessRunner' streamSpec f
data Process m a where
GetArgs :: Process m [Text]
RunProcessCreatingStreams :: Text -> Text -> Process m (ExitCode, Text, Text)
RunProcessInheritingStreams :: Text -> Process m ExitCode
Sem.makeSem ''Process
runProcess ::
(Sem.Member (Sem.Embed IO) effs)
=> Sem.Sem (Process ': effs) a
-> Sem.Sem effs a
runProcess =
Sem.interpret $ \case
GetArgs -> Sem.embed $ map T.pack <$> System.Environment.getArgs
RunProcessCreatingStreams cmd stdin ->
Sem.embed $ do
let stdinStream = P.byteStringInput (T.encodeUtf8Lazy stdin)
let config = P.setStdin stdinStream $ P.shell (T.unpack cmd)
(exitCode, stdout, stderr) <- P.readProcess config
pure (exitCode, T.decodeUtf8Lazy stdout, T.decodeUtf8Lazy stderr)
RunProcessInheritingStreams cmd ->
Sem.embed $ P.runProcess (P.shell $ T.unpack cmd)
execProcess ::
forall (streamSpec :: StreamSpecification) effs. (Sem.Member Process effs)
=> Text
-> ProcessRunner streamSpec (Sem.Sem effs)
execProcess cmd =
case sing :: Sing streamSpec of
SCreateStreams -> runProcessCreatingStreams cmd
SInheritStreams -> runProcessInheritingStreams cmd