{-# LANGUAGE NoImplicitPrelude #-} module System.Command.Monad ( Command(runCommand) , command ) where import Control.Applicative(Applicative((<*>), pure), Alternative((<|>), empty), liftA2) import Control.Monad(Monad(return, (>>=)), MonadPlus(mplus, mzero), liftM) import Control.Monad.Trans.Class(MonadTrans(lift)) import Control.Monad.IO.Class(MonadIO(liftIO)) import Data.Either(Either(Left, Right), either) import Data.Function((.), ($)) import Data.Functor(Functor(fmap)) import Data.String(String) import Data.Monoid(Monoid(mempty)) import System.Command(ExitCode, readProcessWithExitCode, isSuccess) import System.FilePath(FilePath) newtype Command m a = Command { runCommand :: m (Either (ExitCode, String) a) } command :: MonadIO m => FilePath -> [String] -> String -> Command m String command prog args stdin = Command $ do (code, stdout, stderr) <- liftIO $ readProcessWithExitCode prog args stdin return $ if isSuccess code then Right stdout else Left (code, stderr) instance Functor m => Functor (Command m) where fmap f (Command m) = Command (fmap (either Left (Right . f)) m) instance Applicative m => Applicative (Command m) where pure = Command . pure . pure Command mf <*> Command mx = Command (liftA2 (<*>) mf mx) instance Alternative m => Alternative (Command m) where empty = Command (pure (Left mempty)) Command mx <|> Command my = Command (mx <|> my) instance Monad m => Monad (Command m) where return = Command . return . Right Command mx >>= f = Command (mx >>= either (return . Left) (runCommand . f)) instance MonadPlus m => MonadPlus (Command m) where mzero = Command (return (Left mempty)) Command mx `mplus` Command my = Command (mx `mplus` my) instance MonadTrans Command where lift = Command . liftM Right instance MonadIO m => MonadIO (Command m) where liftIO = lift . liftIO