packages feed

chiasma-0.2.0.0: lib/Chiasma/Native/Process.hs

module Chiasma.Native.Process where

import qualified Data.ByteString.Lazy as Lazy (ByteString)
import Data.ByteString.Lazy.Internal (unpackChars)
import qualified Data.Text as Text (intercalate, lines, pack, unwords)
import System.Exit (ExitCode(ExitSuccess))
import System.Process.Typed (ProcessConfig, byteStringInput, proc, readProcessStdout, setStdin)

import Chiasma.Codec.Decode (TmuxDecodeError)
import Chiasma.Data.Cmd (Cmd(..), CmdArgs(..), CmdName(..), Cmds(..))
import Chiasma.Data.TmuxError (TmuxError)
import qualified Chiasma.Data.TmuxError as TmuxError (
  TmuxError(OutputParsingFailed, NoOutput, ProcessFailed, DecodingFailed),
  )
import Chiasma.Native.Parse (resultLines)

cmdBytes :: [Text] -> Lazy.ByteString
cmdBytes cmds = encodeUtf8 $ Text.intercalate "\n" $ reverse $ "" : cmds

socketArg :: Maybe FilePath -> [Text]
socketArg (Just socket) = ["-S", toText socket]
socketArg Nothing = []

tmuxProcessConfig :: Maybe FilePath -> [Text] -> ProcessConfig () () ()
tmuxProcessConfig socket cmds =
  cons args
  where
  cons =
    setStdin (byteStringInput $ cmdBytes cmds) . proc "tmux"
  args =
    toString <$> (socketArg socket <> ["-C", "attach"])

handleProcessOutput :: Cmds -> ExitCode -> (Text -> Either TmuxDecodeError a) -> Text -> Either TmuxError [a]
handleProcessOutput cmds ExitSuccess decode out = do
  outputs <- mapLeft (TmuxError.OutputParsingFailed cmds (Text.lines out)) $ resultLines out
  case reverse outputs of
    output : _ -> traverse decode' output
    _ -> Left $ TmuxError.NoOutput cmds
  where
    decode' = mapLeft (TmuxError.DecodingFailed cmds out) . decode
handleProcessOutput cmds _ _ out =
  Left $ TmuxError.ProcessFailed cmds out

formatCmd :: Cmd -> Text
formatCmd (Cmd (CmdName name) (CmdArgs args)) =
  Text.unwords $ name : args

nativeTmuxProcess ::
  (MonadIO m, MonadDeepError e TmuxError m) =>
  Maybe FilePath ->
  (Text -> Either TmuxDecodeError a) ->
  Cmds ->
  m [a]
nativeTmuxProcess socket decode cmds@(Cmds cmds') = do
  let cmdLines = fmap formatCmd cmds'
  (code, out) <- readProcessStdout $ tmuxProcessConfig socket cmdLines
  hoistEither $ handleProcessOutput cmds code decode $ Text.pack $ unpackChars out