packages feed

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

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

module GHCup.OptParse.Prefetch where


import           GHCup.Command.Prefetch
import           GHCup.Errors
import           GHCup.Types
import           GHCup.Input.Parsers (resolveVersion, toolParser)
import           GHCup.Types.Optics
import           GHCup.Prelude.File
import           GHCup.Prelude.Logger
import           GHCup.Prelude.String.QQ
import           GHCup.OptParse.Common

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

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




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


data PrefetchCommand = PrefetchGHC PrefetchGHCOptions (Maybe ToolVersion)
                     | PrefetchCabal PrefetchOptions (Maybe ToolVersion)
                     | PrefetchHLS PrefetchOptions (Maybe ToolVersion)
                     | PrefetchStack PrefetchOptions (Maybe ToolVersion)
                     | PrefetchTool PrefetchOptions (Maybe ToolVersion) Tool
                     | PrefetchMetadata





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


data PrefetchOptions = PrefetchOptions {
    pfSrc :: Bool
  , pfCacheDir :: Maybe FilePath
}

data PrefetchGHCOptions = PrefetchGHCOptions {
    pfGHCSrc :: Bool
  , pfGHCCacheDir :: Maybe FilePath
}



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


prefetchP :: Parser PrefetchCommand
prefetchP = subparser
  (  command
      "ghc"
      (info
        (PrefetchGHC
          <$> (PrefetchGHCOptions
                <$> ( switch (short 's' <> long "source" <> help "Download source tarball instead of bindist") <**> helper )
                <*> optional (option str (short 'd' <> long "directory" <> help "directory to download into (default: ~/.ghcup/cache/)" <> completer (bashCompleter "directory"))))
          <*>  optional (toolVersionTagArgument [] (Just ghc)) )
        ( progDesc "Download GHC assets for installation")
      )
      <>
     command
      "cabal"
      (info
        (PrefetchCabal
           <$> (PrefetchOptions
                 <$> ( switch (short 's' <> long "source" <> help "Download source tarball instead of bindist") <**> helper )
                 <*> optional (option str (short 'd' <> long "directory" <> help "directory to download into (default: ~/.ghcup/cache/)" <> completer (bashCompleter "directory")))
               )
          <*> ( optional (toolVersionTagArgument [] (Just cabal)) <**> helper ))
        ( progDesc "Download cabal assets for installation")
      )
      <>
     command
      "hls"
      (info
        (PrefetchHLS
           <$> (PrefetchOptions
                 <$> ( switch (short 's' <> long "source" <> help "Download source tarball instead of bindist") <**> helper )
                 <*> optional (option str (short 'd' <> long "directory" <> help "directory to download into (default: ~/.ghcup/cache/)" <> completer (bashCompleter "directory")))
               )
          <*> ( optional (toolVersionTagArgument [] (Just hls)) <**> helper ))
        ( progDesc "Download HLS assets for installation")
      )
      <>
     command
      "stack"
      (info
        (PrefetchStack
           <$> (PrefetchOptions
                 <$> ( switch (short 's' <> long "source" <> help "Download source tarball instead of bindist") <**> helper )
                 <*> optional (option str (short 'd' <> long "directory" <> help "directory to download into (default: ~/.ghcup/cache/)" <> completer (bashCompleter "directory")))
               )
          <*> ( optional (toolVersionTagArgument [] (Just stack)) <**> helper ))
        ( progDesc "Download stack assets for installation")
      )
      <>
     command
      "metadata"
      (PrefetchMetadata <$ info
        helper
        ( progDesc "Download ghcup's metadata, needed for various operations")
      )
  ) <|>
  ( (\o t v -> PrefetchTool o v t)
    <$> (PrefetchOptions
          <$> ( switch (short 's' <> long "source" <> help "Download source tarball instead of bindist") <**> helper )
          <*> optional (option str (short 'd' <> long "directory" <> help "directory to download into (default: ~/.ghcup/cache/)" <> completer (bashCompleter "directory")))
        )
    <*> argument (eitherReader toolParser) (metavar "TOOL" <> help "Which tool to prefetch")
    <*> optional (toolVersionTagArgument [] Nothing)
  )



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


prefetchFooter :: String
prefetchFooter = [s|Discussion:
  Prefetches tools or assets into "~/.ghcup/cache" directory. This can
  be then combined later with '--offline' flag, ensuring all assets that
  are required for offline use have been prefetched.

Examples:
  ghcup prefetch metadata
  ghcup prefetch ghc 8.10.5
  ghcup --offline install ghc 8.10.5|]



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


type PrefetchEffects = '[ TagNotFound
                        , DayNotFound
                        , NextVerNotFound
                        , NoToolVersionSet
                        , NoDownload
                        , DigestError
                        , ContentLengthError
                        , GPGError
                        , DownloadFailed
                        , JSONError
                        , FileDoesNotExistError
                        , StackPlatformDetectError
                        , UnsupportedMetadataFormat
                        , URIParseError
                        , ParseError
                        ]



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



prefetch :: ( Monad m
            , MonadMask m
            , MonadUnliftIO m
            , MonadFail m
            )
         => PrefetchCommand
         -> Settings
         -> (IO (AppState, IO ()), LeanAppState)
         -> m ExitCode
prefetch prefetchCommand settings (getAppState', leanAppstate) =
  run (do
    case prefetchCommand of
      PrefetchGHC
        (PrefetchGHCOptions pfGHCSrc pfCacheDir) mt -> do
          forM_ pfCacheDir (liftIO . createDirRecursive')
          v <- liftE $ resolveVersion mt guessMode ghc
          if pfGHCSrc
          then liftE $ fetchToolSrc ghc v pfCacheDir
          else liftE $ fetchToolBindist v ghc pfCacheDir
      PrefetchCabal PrefetchOptions {pfSrc, pfCacheDir} mt   -> do
        forM_ pfCacheDir (liftIO . createDirRecursive')
        v <- liftE $ resolveVersion mt guessMode cabal
        if pfSrc
        then liftE $ fetchToolSrc cabal v pfCacheDir
        else liftE $ fetchToolBindist v cabal pfCacheDir
      PrefetchHLS PrefetchOptions {pfSrc, pfCacheDir} mt   -> do
        forM_ pfCacheDir (liftIO . createDirRecursive')
        v <- liftE $ resolveVersion mt guessMode hls
        if pfSrc
        then liftE $ fetchToolSrc hls v pfCacheDir
        else liftE $ fetchToolBindist v hls pfCacheDir
      PrefetchStack PrefetchOptions {pfSrc, pfCacheDir} mt   -> do
        forM_ pfCacheDir (liftIO . createDirRecursive')
        v <- liftE $ resolveVersion mt guessMode stack
        if pfSrc
        then liftE $ fetchToolSrc stack v pfCacheDir
        else liftE $ fetchToolBindist v stack pfCacheDir
      PrefetchTool PrefetchOptions {pfCacheDir} mt tool -> do
        forM_ pfCacheDir (liftIO . createDirRecursive')
        v <- liftE $ resolveVersion mt guessMode tool
        liftE $ fetchToolBindist v tool pfCacheDir
      PrefetchMetadata -> do
        pfreq <- lift getPlatformReq
        _ <- liftE $ getDownloadsF pfreq
        pure ""
       ) >>= \case
                (VRight _, up) -> do
                  liftIO up
                  pure ExitSuccess
                (VLeft e, _) -> do
                  runLogger $ logError $ T.pack $ prettyHFError e
                  pure $ ExitFailure 15

 where
  guessMode = if guessVersion settings then GLaxWithInstalled else GStrict

  run action' = do
    (appstate', up) <- liftIO getAppState'
    r <- flip runReaderT appstate'
                  . runResourceT
                  . runE
                    @PrefetchEffects
                  $ action'
    pure (r, up)
  runLogger = flip runReaderT leanAppstate