content: {-# LANGUAGE TemplateHaskell, ScopedTypeVariables, TupleSections, OverloadedStrings #-}
  -- | Invoke the executable that calls cabal functions and communicate
  -- with it via RPC.
  module IdeSession.ExeCabalClient (
      invokeExeCabal
    ) where
  
  import Data.Monoid ((<>))
  import qualified Data.Text as Text
  import System.Exit (ExitCode(..))
  
  import IdeSession.Cabal
  import IdeSession.Config
  import IdeSession.GHC.API
  import IdeSession.RPC.Client (RpcServer, RpcConversation(..), forkRpcServer, rpcConversation, shutdown, findProgram)
  import IdeSession.State
  import IdeSession.Types.Progress
  import IdeSession.Types.Public (UpdateStatus(..))
  import IdeSession.Util
  
  -- | Invoke the executable that processes our custom functions that use
  -- the machinery of the cabal library.
  invokeExeCabal :: IdeStaticInfo -> IdeCallbacks -> ExeCabalRequest -> (UpdateStatus -> IO ())
                 -> IO ExitCode
  invokeExeCabal ideStaticInfo@IdeStaticInfo{..} ideCallbacks args callback = do
    let logFunc = ideCallbacksLogFunc ideCallbacks
    mLoc <- findProgram logFunc searchPath ide_backend_exe_cabal
    case mLoc of
      Nothing ->
        fail $ "Could not find ide-backend-exe-cabal"
      Just prog -> do
        env <- envWithPathOverride configExtraPathDirs
        let cwd = case args of
              ReqExeDoc{} -> ideSessionDir
              _ -> ideDataDir ideStaticInfo
        server <- forkRpcServer prog [] (Just cwd) env
        exitCode <- rpcRunExeCabal server args callback
        shutdown server  -- not long-running
        return exitCode
    where
      (searchPath,ide_backend_exe_cabal) = configIdeBackendExeCabal
  
      SessionConfig{..} = ideConfig
  
  rpcRunExeCabal :: RpcServer -> ExeCabalRequest -> (UpdateStatus -> IO ())
                 -> IO ExitCode
  rpcRunExeCabal server req callback =
    rpcConversation server $ \RpcConversation{..} -> do
      put req
  
      let go = do response <- get
                  case response of
                    ExeCabalProgress pcounter -> do
                      callback (UpdateStatusProgress pcounter)
                      go
                    ExeCabalDone ec@ExitSuccess -> do
                      callback UpdateStatusDone
                      return ec
                    ExeCabalDone ec@(ExitFailure code) -> do
                      callback $ UpdateStatusFailed $
                        "Cabal exited with code " <> Text.pack (show code)
                      return ec
  
      go
  