packages feed

hix-0.7.0: lib/Hix/Managed/EnvResult.hs

module Hix.Managed.EnvResult where

import Data.List.Extra (nubSortOn)
import Data.These (These (That, These, This))
import Data.These.Combinators (justHere, justThere)
import GHC.Generics (Generically (Generically))

import Hix.Class.Map (nElems, nZipWithKey)
import Hix.Data.Version (Version)
import Hix.Data.VersionBounds (VersionBounds)
import Hix.Managed.Data.Diff (
  BoundsChange,
  BoundsDiffDetail (BoundsDiffDetail),
  Change (Changed, Unchanged),
  Diff (DiffAdded, DiffChanged),
  VersionChange,
  )
import Hix.Managed.Data.EnvResult (EnvResult (..))
import qualified Hix.Managed.Data.EnvState
import Hix.Managed.Data.Mutable (MutableDep, MutableDeps)
import qualified Hix.Managed.Data.MutableId
import Hix.Managed.Data.MutableId (MutableId (MutableId))
import Hix.Managed.Data.Mutation (FailedMutation)
import Hix.Managed.Data.StageResult (stageFailures)
import Hix.Managed.Diff (diffOriginal, reifyBoundsChange, reifyVersionChange)
import Hix.These (maybeThese)

newtype BoundsModification =
  BoundsModification (These (Maybe Version) (Maybe Version))
  deriving stock (Eq, Show, Generic)

data DepModification =
  DepAdded (Maybe BoundsModification)
  |
  DepUpdated (These Version BoundsModification)
  deriving stock (Eq, Show, Generic)

data DepResultDetail =
  DepModified DepModification
  |
  DepUnmodified
  deriving stock (Eq, Show, Generic)

data DepResult =
  DepResult {
    package :: MutableDep,
    version :: Version,
    bounds :: VersionBounds,
    detail :: DepResultDetail
  }
  deriving stock (Eq, Show, Generic)

depResultId :: DepResult -> MutableId
depResultId DepResult {package, version} =
  MutableId {name = package, version}

depResult :: MutableDep -> VersionChange -> BoundsChange -> Maybe DepResult
depResult package versionChange boundsChange = do
  version <- reifyVersionChange versionChange
  pure DepResult {
    package,
    version,
    bounds = reifyBoundsChange boundsChange,
    detail
  }
  where
    detail = case versionChange of
      Changed (DiffAdded _) -> DepModified (DepAdded boundsUpdate)
      Changed (DiffChanged original _ _) ->
        DepModified (DepUpdated (maybe This (flip These) boundsUpdate original))
      Unchanged _
        | Just b <- boundsUpdate
        -> DepModified (DepUpdated (That b))
      _ -> DepUnmodified

    boundsUpdate = case boundsChange of
      Changed (DiffChanged _ _ (BoundsDiffDetail det)) ->
        BoundsModification <$> maybeThese (diffOriginal <$> justHere det) (diffOriginal <$> justThere det)
      _ -> Nothing

deps :: EnvResult -> [DepResult]
deps EnvResult {state = Nothing} =
  []
deps EnvResult {state = Just state} =
  catMaybes (nElems dv)
  where
    dv :: MutableDeps (Maybe DepResult)
    dv = nZipWithKey depResult state.versions state.bounds

data DepResults =
  DepResults {
    added :: [DepResult],
    updated :: [DepResult],
    unmodified :: [DepResult]
  }
  deriving stock (Eq, Show, Generic)
  deriving (Semigroup, Monoid) via (Generically DepResults)

-- TODO When this is used after merging envs for a report, it should probably combine packages from different lists.
-- If one env updates a package, we don't want to report it as unmodified because there's another env with the same dep.
-- Most likely the output should be env-keyed though, but not sure this is always desirable.
normalizeDepResults :: DepResults -> DepResults
normalizeDepResults DepResults {..} =
  DepResults {added = normalize added, updated = normalize updated, unmodified = normalize unmodified}
  where
    normalize = nubSortOn (.package)

grouped :: EnvResult -> DepResults
grouped result =
  normalizeDepResults DepResults {..}
  where
    (added, updated, unmodified) = foldr (flip step) mempty (deps result)

    step (a, up, un) dep =
      case dep.detail of
        DepModified DepAdded {} -> (dep : a, up, un)
        DepModified DepUpdated {} -> (a, dep : up, un)
        DepUnmodified -> (a, up, dep : un)

failures :: EnvResult -> [FailedMutation]
failures EnvResult {summaries} =
  stageFailures =<< toList summaries