packages feed

ghcup-0.2.1.0: lib/GHCup/Query/Symlink.hs

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}

module GHCup.Query.Symlink where

import GHCup.Errors
import GHCup.Legacy.Utils
import GHCup.Prelude
import GHCup.Types
import GHCup.Types.Optics

import Control.Exception      ( Exception (..) )
import Control.Exception.Safe ( handle )
import Control.Monad          ( forM, when )
import Control.Monad.Reader   ( MonadReader )
import Data.Either            ( partitionEithers )
import Data.Maybe             ( catMaybes )
import Data.Variant.Excepts   ( Excepts, throwE )
import Data.Versions          ( Version, prettyVer )
import System.FilePath        ( (</>) )

import qualified Data.Text as T


-- | These are the symlinks we need to create
-- in order to 'set' a tool version.
getUnqualifiedSymlinks ::
  ( MonadReader env m
  , HasDirs env
  , HasLog env
  , MonadIOish m
  )
  => [SymlinkFileSpec]
  -> FilePath
  -> m [(FilePath, FilePath)]
getUnqualifiedSymlinks specs toolDir = do
  Dirs {..}  <- getDirs
  getUnqualifiedSymlinks' binDir specs toolDir

-- | Like 'getUnqualifiedSymlinks', except takes
-- the binary directory as an explicit argument.
getUnqualifiedSymlinks' ::
  ( MonadReader env m
  , HasDirs env
  , HasLog env
  , MonadIOish m
  )
  => FilePath
  -> [SymlinkFileSpec]
  -> FilePath
  -> m [(FilePath, FilePath)]
getUnqualifiedSymlinks' bindir specs toolDir =
  fmap catMaybes $ forM specs $ \SymlinkSpec{..} ->
    case _slSetName of
      Nothing -> pure Nothing
      Just setName -> do
       target <- binarySymLinkDestination bindir (toolDir </> _slTarget)
       pure $ Just (target, bindir </> setName)

getPVPMajorSymlinks' ::
  ( MonadReader env m
  , HasLog env
  , MonadIOish m
  )
  => FilePath
  -> [SymlinkSpec [Either Char Version]]
  -> Version
  -> FilePath
  -> Excepts '[MalformedInstallInfo] m [(FilePath, FilePath)]
getPVPMajorSymlinks' bindir spec ver toolBinDir =
  fmap catMaybes $ forM spec $ \SymlinkSpec{..} -> do
    if _slPVPMajorLinks
    then do
      let target = mconcat $ either (:[]) (T.unpack . prettyVer) <$> _slTarget
      targetResolved <- binarySymLinkDestination bindir (toolBinDir </> target)
      handle
          (\(e :: ParseError) -> logWarn (T.pack $ displayException e) >> pure Nothing)
        $ do
          when (null . snd . partitionEithers $ _slLinkName) $ throwE $ MalformedInstallInfo "No ${PKGVER} found in linkName"
          (mj, mi) <- getMajorMinorV ver
          let major' = T.unpack (intToText mj <> "." <> intToText mi)
              link_name = mconcat $ either (:[]) (const major') <$> _slLinkName
          logDebug2 $ T.pack (show _slLinkName)
          logDebug2 $ T.pack (show link_name)
          logDebug2 $ T.pack (show major')

          -- if our tool has only two components anyway, we skip this
          if (intToText mj <> "." <> intToText mi) == prettyVer ver
          then pure Nothing
          else pure (Just (targetResolved, bindir </> link_name))
    else pure Nothing

getPVPMajorSymlinks ::
  ( MonadReader env m
  , HasDirs env
  , HasLog env
  , MonadIOish m
  )
  => [SymlinkSpec [Either Char Version]]
  -> Version
  -> FilePath
  -> Excepts '[MalformedInstallInfo] m [(FilePath, FilePath)]
getPVPMajorSymlinks specs ver toolBinDir = do
  Dirs {..}  <- getDirs
  getPVPMajorSymlinks' binDir specs ver toolBinDir

getPVPSymlinks' ::
  ( MonadReader env m
  , HasLog env
  , MonadIOish m
  )
  => FilePath
  -> [SymlinkFileSpec]
  -> FilePath
  -> m [(FilePath, FilePath)]
getPVPSymlinks' bindir specs toolDir = do
  forM specs $ \SymlinkSpec{..} -> do
      target <- binarySymLinkDestination bindir (toolDir </> _slTarget)
      pure (target, bindir </> _slLinkName)

getPVPSymlinks ::
  ( MonadReader env m
  , HasDirs env
  , HasLog env
  , MonadIOish m
  )
  => [SymlinkFileSpec]
  -> FilePath
  -> m [(FilePath, FilePath)]
getPVPSymlinks specs toolDir = do
  Dirs {..}  <- getDirs
  getPVPSymlinks' binDir specs toolDir