packages feed

hix-0.9.0: lib/Hix/Managed/ProjectStateProto.hs

module Hix.Managed.ProjectStateProto where

import Data.Map.Merge.Strict (mapMissing, traverseMaybeMissing, traverseMissing, zipWithAMatched, zipWithMatched)
import Exon (exon)

import Hix.Class.Map (NMap, nBy, nElems, nFromKeys, nMap, nMergeA, nTransform)
import Hix.Data.Bounds (Bounds, Ranges)
import Hix.Data.EnvName (EnvName)
import Hix.Data.Monad (M)
import Hix.Data.Options (ProjectOptions (..))
import Hix.Data.PackageName (LocalPackage, PackageName)
import Hix.Data.Version (Version, Versions)
import Hix.Data.VersionBounds (amendUpper, anyBounds)
import qualified Hix.Log as Log
import qualified Hix.Managed.Data.EnvContext
import Hix.Managed.Data.EnvContext (EnvDeps (EnvDeps))
import Hix.Managed.Data.Envs (Envs)
import Hix.Managed.Data.Mutable (MutableDep, depName)
import Hix.Managed.Data.Packages (Deps, Packages)
import qualified Hix.Managed.Data.ProjectState
import Hix.Managed.Data.ProjectState (ProjectState (ProjectState))
import qualified Hix.Managed.Data.ProjectStateProto
import Hix.Managed.Data.ProjectStateProto (ProjectStateProto)
import Hix.Version (upperVersion)

invalidDep ::
  ∀ a b .
  LocalPackage ->
  PackageName ->
  a ->
  M (Maybe b)
invalidDep package dep _ =
  Nothing <$ Log.warn [exon|Discarding bound for invalid dep '##{dep}' of package '##{package}'|]

envDepsForMerge :: Envs EnvDeps -> Envs (Deps MutableDep)
envDepsForMerge =
  nMap \ EnvDeps {mutable} -> nBy mutable depName

toMutable ::
  NMap map MutableDep a sort =>
  Deps (MutableDep, a) ->
  map
toMutable = nTransform \ _ -> id

invalidBoundsPackage :: LocalPackage -> a -> M (Maybe b)
invalidBoundsPackage package _ =
  Nothing <$ Log.warn [exon|Discarding bounds for unknown local package '##{package}'|]

validateBounds ::
  Bool ->
  LocalPackage ->
  Ranges ->
  Bounds ->
  M Bounds
validateBounds readUpper package ranges stateBounds =
  nMergeA stateMissing depMissing convertBound ranges stateBounds
  where
    stateMissing = mapMissing \ _ range -> handleUpper range anyBounds

    depMissing = traverseMaybeMissing (invalidDep package)

    convertBound = zipWithMatched (const handleUpper)

    handleUpper range | readUpper, Just u <- upperVersion range = amendUpper u
                      | otherwise = id

validateProjectBounds ::
  Bool ->
  Packages Ranges ->
  Packages Bounds ->
  M (Packages Bounds)
validateProjectBounds readUpper =
  nMergeA boundsMissing (traverseMaybeMissing invalidBoundsPackage) (zipWithAMatched (validateBounds readUpper))
  where
    boundsMissing = traverseMissing \ name package -> validateBounds readUpper name package mempty

invalidStateDep :: Text -> EnvName -> PackageName -> a -> M (Maybe b)
invalidStateDep desc env package _ =
  Nothing <$ Log.warn [exon|Discarding #{desc} for unknown dep '##{package}' of env '##{env}'|]

validateVersions ::
  NMap map MutableDep (Maybe Version) sort =>
  Text ->
  EnvName ->
  Deps MutableDep ->
  Versions ->
  M map
validateVersions desc env deps bounds =
  toMutable <$> nMergeA stateMissing envMissing matching deps bounds
  where
    stateMissing = mapMissing \ _ dep -> (dep, Nothing)
    envMissing = traverseMaybeMissing (invalidStateDep desc env)
    matching = zipWithMatched \ _ dep version -> (dep, Just version)

-- TODO ugly
emptyVersions ::
  NMap map MutableDep (Maybe Version) sort =>
  Deps MutableDep ->
  map
emptyVersions deps =
  nFromKeys (nElems deps) (const Nothing)

invalidVersions :: Text -> EnvName -> a -> M (Maybe b)
invalidVersions desc env _ =
  Nothing <$ Log.warn [exon|Discarding #{desc} for unknown env '##{env}'|]

validateProjectVersions ::
  NMap map MutableDep (Maybe Version) sort =>
  Text ->
  Envs (Deps MutableDep) ->
  Envs Versions ->
  M (Envs map)
validateProjectVersions desc =
  nMergeA stateMissing envMissing matching
  where
    stateMissing = mapMissing (const emptyVersions)
    envMissing = traverseMaybeMissing (invalidVersions desc)
    matching = zipWithAMatched (validateVersions desc)

validateProjectState ::
  ProjectOptions ->
  Packages Ranges ->
  Envs EnvDeps ->
  ProjectStateProto ->
  M ProjectState
validateProjectState opts ranges envDeps proto = do
  bounds <- validateProjectBounds opts.readUpperBounds ranges proto.bounds
  versions <- validateProjectVersions "bound versions" depSets proto.versions
  initial <- validateProjectVersions "initial versions" depSets proto.initial
  pure ProjectState {overrides = proto.overrides, solver = proto.solver, resolving = False, ..}
  where
    depSets = envDepsForMerge envDeps