packages feed

haskell-updater-1.5.0.0: src/Distribution/Gentoo/Types/Mode.hs

{- |
   Module      : Distribution.Gentoo.Types.Mode
   Description : Valid modes for haskell-updater

   This module houses a complex ADT which represents valid modes for
   haskell-updater. This is converted from @CmdLineArgs@ (in
   @Distribution.Gentoo.CmdLine.Types@), which represents
   possible options given on the command line.
 -}

{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE FlexibleInstances #-}

module Distribution.Gentoo.Types.Mode
    ( HUMode(..)
    , PkgManager(..)
    , RunMode(..)
    , runMode
    , Target(..)
    , getTarget
    , PortageMode(..)
    , PortageBasicTarget(..)
    , ReinstallAtomsTarget(..)
    , RATargets(..)
    , getLoopType
    , getExtraRawArgs
    ) where

import Data.Bifoldable (bifoldMap)
import Data.Monoid (Last(..))

import Distribution.Gentoo.Types hiding (Target)
import Distribution.Gentoo.Util (These(..), NESet(..))
import Distribution.Gentoo.PkgManager.Types (PMFlag(..))

-- | Top level run modes for @haskell-updater@ including @--help@ and
--   @--version@, which do not run the @runUpdater@ function in "Main".
--   v'RunMode' is used during normal use of the utility.
data HUMode
    = HelpMode
    | VersionMode
    | RunMode RunModifier PkgManager
    deriving (Eq, Ord, Show)

-- | Choice of supported package managers. Currently, portage has more modes
--   available, such as @--mode=reinstall-atoms@, which are encoded in
--   'PortageMode'.
data PkgManager
    = Portage PortageMode
    | PkgCore RunMode
    | Paludis RunMode
    | CustomPM String RunMode
    deriving (Eq, Ord, Show)

-- | Basic run modes that are available for all package managers.
data RunMode
    = BasicMode Target
    | ListMode Target
    deriving (Eq, Ord, Show)

-- | Basic targets that are available for all package managers.
data Target
    = OnlyInvalid
    | AllInstalled
    deriving (Eq, Ord, Show)

-- | Custom target to be passed to the package manager.
type CustomTarget = String

-- | Encodes valid target combinations for 'ReinstallAtomsMode' (portage only).
newtype RATargets = RATargets
    { unRATargets :: These Target
        (These ReinstallAtomsTarget (NESet CustomTarget)) }
    deriving (Show, Eq, Ord)

-- | No Monoid instance since there is intentionally no empty element
instance Semigroup RATargets where
    sel1 <> sel2 = RATargets $ case conv (toMonoidTriple sel1 <> toMonoidTriple sel2) of
        (Just t, Just r, Just s) -> These t (These r s)
        (Just t, Just r, Nothing) -> These t (This r)
        (Just t, Nothing, Just s) -> These t (That s)
        (Just t, Nothing, Nothing) -> This t
        (Nothing, Just r, Just s) -> That (These r s)
        (Nothing, Just r, Nothing) -> That (This r)
        (Nothing, Nothing, Just s) -> That (That s)
        -- If there was an option for no targets, it would go here
        (Nothing, Nothing, Nothing) -> undefined
      where
        conv (Last mt, Last mr, ms) = (mt,mr,ms)

        -- | Convenience function to turn RATargets into a triple of monoids, which helps
        --   in the Semigroup definition.
        toMonoidTriple
            :: RATargets
            -> (Last Target, Last ReinstallAtomsTarget, Maybe (NESet CustomTarget))
        toMonoidTriple = bifoldMap
            (\t -> (pure t, mempty, mempty))
            (bifoldMap
                (\r -> (mempty, pure r, mempty))
                (\s -> (mempty, mempty, pure s))
            ) . unRATargets

-- | Modes that are available for portage. This includes the basic
--   'PortageBasicMode' and 'PortageListMode' (which correspond to
--   'BasicMode' and 'ListMode' respectively), but also 'ReinstallAtomsMode',
--   which is specific to portage.
data PortageMode
    = PortageBasicMode (Either PortageBasicTarget Target)
    | PortageListMode Target
    | ReinstallAtomsMode RATargets
    deriving (Eq, Ord, Show)

-- | Extra targets that are available for portage in @--mode=basic@. Currently,
--   this only includes @@preserved-rebuild@.
data PortageBasicTarget = PreservedRebuild
    deriving (Eq, Ord, Show)

-- | Targets that are availble only for @--mode=reinstall-atoms' (portage
--   only). This includes 'WorldTarget' (@@world@) and 'WorldFullTarget', a
--   special convenience target which adds @@world@ to the target list and
--   also passes @--newuse --with-bdeps=y@ to portage for convenience.
--   (See 'getExtraRawArgs').
data ReinstallAtomsTarget
    = WorldTarget
    | WorldFullTarget
    deriving (Eq, Ord, Show)

-- | Extract the t'RunMode' (or 'PortageMode') from the 'HUMode' ADT.
--
--   Like other functions in this module, it takes a 'PkgManager' (as opposed
--   to 'HUMode'), since only the v'RunMode' constructor of 'HUMode' is
--   relevant.
runMode :: PkgManager -> Either RunMode PortageMode
runMode (Portage rm) = Right rm
runMode (PkgCore rm) = Left rm
runMode (Paludis rm) = Left rm
runMode (CustomPM _ rm) = Left rm

-- | Extract the 'Target' from a t'RunMode' constructor.
getTarget :: RunMode -> Target
getTarget (BasicMode t) = t
getTarget (ListMode t) = t

-- | Convert from the @haskell-updater@ ADT to 'LoopMode', which encodes
--   how the looping mechanism of @haskell-updater@ should funciton.
--
--   Like other functions in this module, it takes a 'PkgManager' (as opposed
--   to 'HUMode'), since only the v'RunMode' constructor of 'HUMode' is
--   relevant.
getLoopType :: RunModifier -> PkgManager -> LoopType
getLoopType rm
      -- Always use NoLoop if --pretend is passed on the command line
    | any (== PretendBuild) (flags rm) = const NoLoop
    | otherwise = \case
        -- ReinstallAtomsMode should always use UntilNoChange
        Portage (ReinstallAtomsMode _) -> UntilNoChange

        -- @--target=preserved-rebuild@ should use UntilNoChange
        Portage (PortageBasicMode (Left PreservedRebuild)) -> UntilNoChange

        -- The rest follow a standard pattern
        Portage (PortageBasicMode (Right t)) -> fromTarget t
        Portage (PortageListMode _) -> NoLoop
        PkgCore mode -> fromRunMode mode
        Paludis mode -> fromRunMode mode
        CustomPM _ mode -> fromRunMode mode
  where
    fromRunMode :: RunMode -> LoopType
    fromRunMode = \case
        BasicMode t -> fromTarget t
        ListMode _ -> NoLoop

    fromTarget :: Target -> LoopType
    fromTarget = \case
        OnlyInvalid -> UntilNoPending
        AllInstalled -> NoLoop

-- | Convert from the 'HUMode' ADT to 'ExtraRawArgs', hard-coded extra
--   arguments that will be passed to the package manager.
--
--   Like other functions in this module, it takes a 'PkgManager' (as opposed
--   to 'HUMode'), since only the v'RunMode' constructor of 'HUMode' is
--   relevant.
getExtraRawArgs :: PkgManager -> ExtraRawArgs
getExtraRawArgs = ExtraRawArgs . \case
    Portage (ReinstallAtomsMode (RATargets t)) ->
        bifoldMap (const []) (bifoldMap fromRAT (const [])) t
    Portage (PortageBasicMode _) -> ["--usepkg-exclude=*/*"]
    _ -> []
  where
    fromRAT = \case
        WorldFullTarget -> ["--newuse", "--with-bdeps=y"]
        WorldTarget -> []