packages feed

hix-0.7.2: lib/Hix/Managed/Build/Mutation.hs

module Hix.Managed.Build.Mutation where

import Control.Lens ((.~))
import Exon (exon)

import Hix.Class.Map (nAdjust, nAmendWithKey)
import Hix.Data.Monad (M)
import qualified Hix.Data.PackageId
import Hix.Data.PackageId (PackageId (PackageId))
import Hix.Data.Version (Version)
import Hix.Data.VersionBounds (VersionBounds, exactVersion)
import qualified Hix.Log as Log
import Hix.Managed.Cabal.Data.SolverState (SolverState, updateSolverState)
import Hix.Managed.Data.Constraints (EnvConstraints, MutationConstraints)
import Hix.Managed.Data.Mutable (MutableDep, depName, mutRelax)
import qualified Hix.Managed.Data.MutableId
import Hix.Managed.Data.MutableId (MutableId (MutableId))
import qualified Hix.Managed.Data.Mutation
import Hix.Managed.Data.Mutation (BuildMutation (BuildMutation))
import qualified Hix.Managed.Data.MutationState
import Hix.Managed.Data.MutationState (MutationState)
import Hix.Managed.Data.Packages (Deps)
import Hix.Pretty (showP)

candidateMutation ::
  SolverState ->
  MutableId ->
  (Version -> VersionBounds -> VersionBounds) ->
  BuildMutation
candidateMutation solverState candidate updateBound =
  BuildMutation {
    description = showP candidate,
    solverState,
    updateBound
  }

candidateConstraints :: MutableId -> EnvConstraints -> EnvConstraints
candidateConstraints MutableId {..} constraints =
  nAdjust (depName name) constraints (#mutation .~ exactVersion version)

updateConstraints ::
  (MutableId -> PackageId -> MutationConstraints -> MutationConstraints) ->
  MutableId ->
  MutationState ->
  EnvConstraints ->
  EnvConstraints
updateConstraints impl candidate state =
  nAmendWithKey update (mutRelax state.versions :: Deps (Maybe Version))
  where
    update name = \case
      Just version -> impl candidate PackageId {..}
      Nothing -> id

-- | Boilerplate for mutation handlers, could be better.
--
-- TODO If we'd use the @retract@ field from @DepMutation@ and the target bound here, we could probably use a universal
-- bounds updater without leaking implementation...investigate.
buildCandidate ::
  (BuildMutation -> M (Maybe (MutationState, Set PackageId))) ->
  (Version -> VersionBounds -> VersionBounds) ->
  (MutableId -> PackageId -> MutationConstraints -> MutationConstraints) ->
  SolverState ->
  MutableDep ->
  Version ->
  M (Maybe (MutableId, SolverState, MutationState, Set PackageId))
buildCandidate build updateStateBound updateConstraintBound solverState package version = do
  Log.debug [exon|Mutation constraints for #{showP candidate}: #{showP mutationSolverState.constraints}|]
  fmap result <$> build (candidateMutation mutationSolverState candidate updateStateBound)
  where
    result (newState, revisions) = (candidate, newSolverState newState, newState, revisions)

    candidate = MutableId {name = package, version}

    mutationSolverState = updateSolverState (candidateConstraints candidate) solverState

    newSolverState newState =
      updateSolverState (updateConstraints updateConstraintBound candidate newState) solverState