packages feed

handsy-0.0.13.1: src/System/Handsy/Internal.hs

{-# LANGUAGE GADTs               #-}
{-# LANGUAGE LambdaCase          #-}
{-# LANGUAGE ScopedTypeVariables #-}

module System.Handsy.Internal where

import           Control.Error
import           Control.Exception.Lifted  (bracket)
import           Control.Monad
import           Control.Monad.IO.Class
import           Control.Monad.Operational
import           Control.Monad.Trans.Class
import qualified Data.ByteString.Lazy      as B
import           Data.Default.Class
import           System.Exit               (ExitCode)
import           System.IO                 (hPutStrLn, stderr)

type StdOut = B.ByteString
type StdErr = B.ByteString

data HandsyInstruction a where
 Shell :: String -> B.ByteString -> HandsyInstruction (ExitCode, StdOut, StdErr)

type Handsy a = ProgramT HandsyInstruction Script a

shellF :: FilePath -> B.ByteString -> Handsy (ExitCode, B.ByteString, B.ByteString)
shellF cmd stdin = singleton $ Shell cmd stdin

data Options =
  Options { debug :: Bool -- ^ Log commands to stderr before running
          }

instance Default Options where
  def = Options False

interpret :: forall r . forall a
           . Script r         -- ^ Acquire resource
          -> (r -> Script ()) -- ^ Release resource
          -> (r -> String -> B.ByteString -> Script (ExitCode, B.ByteString, B.ByteString))
          -> Options
          -> Handsy a
          -> EitherT String IO a
interpret acquire destroy f opts handsy = bracket acquire destroy (`go` handsy)
  where go :: r -> Handsy a -> Script a
        go res h = viewT h >>= \case
          Return x                   -> right x
          Shell cmdline stdin :>>= k -> when (debug opts) (liftIO $ hPutStrLn stderr cmdline)
                                        >> f res cmdline stdin >>= go res . k

interpretSimple :: (FilePath -> B.ByteString -> Script (ExitCode, B.ByteString, B.ByteString)) -- ^ 'readProcessWithExitCode'
                -> Options
                -> Handsy a
                -> Script a
interpretSimple f = interpret (return ()) (const (return ())) (const f)

handsyIO :: IO a -> Handsy a
handsyIO = lift . scriptIO

handsyLeft :: String -> Handsy a
handsyLeft = lift . left