module Chromatin.Rebuild.Build( installRplugin, InstallResult(..), venvDir, ) where import Control.Monad.IO.Class (MonadIO) import Data.List.Split (linesBy) import GHC.IO.Exception (ExitCode(ExitFailure)) import GHC.IO.Handle.Types (Handle) import qualified Data.ByteString.Lazy.Internal as B (unpackChars) import System.FilePath (()) import System.Process.Typed (ProcessConfig, setStdout, useHandleClose, readProcessStderr, proc, setWorkingDir) import UnliftIO.Temporary (withSystemTempFile) import UnliftIO.Directory ( getXdgDirectory, XdgDirectory(XdgData, XdgCache), createDirectoryIfMissing, removePathForcibly, ) import Neovim (Buffer, vim_command', vim_get_current_buffer', vim_command, buffer_get_number') import Ribosome.Api.Echo (echom) import Chromatin.Data.Rplugin (Rplugin(Rplugin)) import Chromatin.Data.RpluginName (RpluginName(..)) import Chromatin.Data.RpluginSource (RpluginSource(Hackage, Stack, Pypi), HackageDepspec(..), PypiDepspec(..)) import Chromatin.Data.Chromatin (Chromatin) data InstallResult = Success Rplugin | Failure [String] deriving (Show, Eq) tailInTerminal :: FilePath -> Chromatin Buffer tailInTerminal path = do vim_command' $ "15split term://tail -f " ++ path vim_get_current_buffer' closeTerminal :: Buffer -> Chromatin () closeTerminal buf = do num <- buffer_get_number' buf _ <- vim_command $ "silent! " ++ show num ++ "bwipeout!" return () processWithStdoutFile :: MonadIO m => ProcessConfig stdin stdout stderr -> Handle -> m (Either String ()) processWithStdoutFile processConfig logHandle = do let pipe = setStdout (useHandleClose logHandle) (code, err) <- readProcessStderr $ pipe processConfig return $ case code of ExitFailure _ -> Left (B.unpackChars err) _ -> Right () hackageProcess :: FilePath -> HackageDepspec -> ProcessConfig () () () hackageProcess bindir (HackageDepspec spec) = proc "cabal" ["new-install", "--symlink-bindir", bindir, spec] stackProcess :: FilePath -> ProcessConfig () () () stackProcess path = setWorkingDir path $ proc "stack" ["build"] processWithLog :: RpluginName -> ProcessConfig stdin stdout sderr -> Chromatin (Either String ()) processWithLog (RpluginName name) processConfig = withSystemTempFile "test-chromatin" $ \logFile logHandle -> do buf <- tailInTerminal logFile result <- processWithStdoutFile processConfig logHandle case result of Right _ -> do echom $ "installed `" ++ name ++ "`" closeTerminal buf Left _ -> echom $ "failed to install `" ++ name ++ "`" return result installHackageProcess :: RpluginName -> FilePath -> HackageDepspec -> Chromatin (Either String ()) installHackageProcess name bindir spec = processWithLog name $ hackageProcess bindir spec installStackProcess :: RpluginName -> FilePath -> Chromatin (Either String ()) installStackProcess name path = processWithLog name $ stackProcess path venvProcess :: FilePath -> ProcessConfig () () () venvProcess dir = proc "python3" ["-m", "venv", dir, "--upgrade"] venvDir :: RpluginName -> Chromatin FilePath venvDir (RpluginName name) = getXdgDirectory XdgCache ("chromatin-hs" "venvs" name) createVenvProcess :: RpluginName -> Chromatin (Either String FilePath) createVenvProcess name = do dir <- venvDir name removePathForcibly dir r <- processWithLog name (venvProcess dir) return $ dir <$ r pipProcess :: PypiDepspec -> FilePath -> ProcessConfig () () () pipProcess (PypiDepspec spec) venv = proc (venv "bin" "pip") ["install", "--no-cache", "--upgrade", spec] installPypiProcess :: RpluginName -> PypiDepspec -> FilePath -> Chromatin (Either String ()) installPypiProcess name spec venv = processWithLog name (pipProcess spec venv) binaryDir :: Chromatin FilePath binaryDir = getXdgDirectory XdgData ("chromatin" "bin") installRpluginFromSource :: RpluginName -> RpluginSource -> Chromatin (Either String ()) installRpluginFromSource name (Hackage spec) = do bindir <- binaryDir createDirectoryIfMissing True bindir installHackageProcess name bindir spec installRpluginFromSource name (Stack path) = installStackProcess name path installRpluginFromSource name (Pypi spec) = do dir <- createVenvProcess name case dir of Right d -> installPypiProcess name spec d Left e -> return $ Left e installRplugin :: RpluginName -> RpluginSource -> Chromatin InstallResult installRplugin name source = do result <- installRpluginFromSource name source return $ case result of Right _ -> Success $ Rplugin name source Left err -> Failure $ linesBy (=='\n') err