packages feed

ghcup-0.2.1.0: lib-opt/GHCup/OptParse/Rm.hs

{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ViewPatterns #-}

module GHCup.OptParse.Rm where




import GHCup.Command.List
import GHCup.Command.Rm
import GHCup.Errors
import GHCup.Input.Parsers     ( ghcVersionEither, toolParser )
import GHCup.OptParse.Common
import GHCup.Prelude.Logger
import GHCup.Prelude.String.QQ
import GHCup.Query.Metadata
import GHCup.Types
import GHCup.Types.Optics

#if !MIN_VERSION_base(4,13,0)
import Control.Monad.Fail ( MonadFail )
#endif
import Control.Monad                  ( forM_, when )
import Control.Monad.Reader
import Control.Monad.Trans.Resource
import Data.Functor
import Data.Maybe
import Data.Variant.Excepts
import Data.Versions
import Options.Applicative            hiding ( ParseError, style )
import Prelude                        hiding ( appendFile )
import System.Exit
import Text.PrettyPrint.HughesPJClass ( prettyShow )

import           Control.Exception.Safe ( MonadMask )
import qualified Data.Text              as T




    ----------------
    --[ Commands ]--
    ----------------


data RmCommand
  = RmGHC RmOptions
  | RmCabal Version
  | RmHLS Version
  | RmStack Version
  | RmOther RmOptionsNew
  deriving (Eq, Show)




    ---------------
    --[ Options ]--
    ---------------


data RmOptions = RmOptions
  { ghcVer :: TargetVersion
  }
  deriving (Eq, Show)

data RmOptionsNew = RmOptionsNew
  { rmTool :: Tool
  , ghcVer :: TargetVersion
  }
  deriving (Eq, Show)




    ---------------
    --[ Parsers ]--
    ---------------


rmParser :: Parser RmCommand
rmParser =
  subparser
      (  command
          "ghc"
          (RmGHC <$> info (rmOpts (Just ghc) <**> helper) (progDesc "Remove GHC version"))
      <> command
           "cabal"
           (   RmCabal
           <$> info (versionParser' [ListInstalled True] (Just cabal) <**> helper)
                    (progDesc "Remove Cabal version")
           )
      <> command
           "hls"
           (   RmHLS
           <$> info (versionParser' [ListInstalled True] (Just hls) <**> helper)
                    (progDesc "Remove haskell-language-server version")
           )
      <> command
           "stack"
           (   RmStack
           <$> info (versionParser' [ListInstalled True] (Just stack) <**> helper)
                    (progDesc "Remove stack version")
           )
      )
    <|> (   RmOther
           <$> rmOptsNew
           )



rmOpts :: Maybe Tool -> Parser RmOptions
rmOpts tool = RmOptions <$> ghcVersionArgument [ListInstalled True] tool

rmOptsNew :: Parser RmOptionsNew
rmOptsNew = RmOptionsNew
  <$> argument (eitherReader toolParser) (metavar "TOOL")
  <*> argument (eitherReader ghcVersionEither) (metavar "VERSION")





    --------------
    --[ Footer ]--
    --------------


rmFooter :: String
rmFooter = [s|Discussion:
  Remove the given GHC or cabal version. When no command is given,
  defaults to removing GHC with the specified version.
  It is recommended to always specify a subcommand (ghc/cabal/hls/stack).|]




    ---------------------------
    --[ Effect interpreters ]--
    ---------------------------


type RmEffects = '[ NotInstalled, UninstallFailed, ParseError, MalformedInstallInfo ]


runRm :: (ReaderT env m (VEither RmEffects a) -> m (VEither RmEffects a))
            -> Excepts RmEffects (ReaderT env m) a
            -> m (VEither RmEffects a)
runRm runAppState =
    runAppState
    . runE
      @RmEffects



    ------------------
    --[ Entrypoint ]--
    ------------------



rm :: ( Monad m
      , MonadMask m
      , MonadUnliftIO m
      , MonadFail m
      )
   => RmCommand
   -> (IO (AppState, IO ()), LeanAppState)
   -> m ExitCode
rm rmCommand (getAppState', leanAppstate) = case rmCommand of
  (RmGHC rmopts) -> rmOther (toRmOptionsNew ghc rmopts)
  (RmCabal (RmOptions . mkTVer -> rmopts)) -> rmOther (toRmOptionsNew cabal rmopts)
  (RmHLS (RmOptions . mkTVer -> rmopts)) -> rmOther (toRmOptionsNew hls rmopts)
  (RmStack (RmOptions . mkTVer -> rmopts)) -> rmOther (toRmOptionsNew stack rmopts)
  (RmOther rmopts) -> rmOther rmopts

 where
  toRmOptionsNew rmTool RmOptions{..} = RmOptionsNew{..}
  runLogger = flip runReaderT leanAppstate
  run action' = do
    (appstate', up) <- liftIO getAppState'
    r <- flip runReaderT appstate'
                  . runResourceT
                  . runE
                    @RmEffects
                  $ action'
    pure (r, up)

  rmOther RmOptionsNew{..} =
    run (do
        liftE $
          rmToolVersion rmTool ghcVer
        GHCupInfo { _ghcupDownloads = dls } <- lift getGHCupInfo
        pure (getVersionMetadata ghcVer rmTool dls)
      )
      >>= \case
            (VRight vm, up) -> do
              postRmLog (tVerToText ghcVer) rmTool vm
              when (rmTool == ghc) $ runLogger $ logGHCPostRm ghcVer
              liftIO up
              pure ExitSuccess
            (VLeft e, _) -> do
              runLogger $ logError $ T.pack $ prettyHFError e
              pure $ ExitFailure 7

  postRmLog tv tool vm = runLogger $ do
    logInfo $ "Successfully removed " <> T.pack (prettyShow tool) <> " " <> tv
    forM_ (_vmPostRemove =<< vm) logInfo