packages feed

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

module Hix.Managed.ProjectContextProto where

import qualified Data.Set as Set
import Exon (exon)

import Hix.Class.Map (nConcat, nGen, nKeys, nKeysSet, nMap, nOver, (!?))
import Hix.Data.Dep (Dep (..))
import Hix.Data.EnvName (EnvName)
import Hix.Data.Error (ErrorMessage (Client))
import Hix.Data.Monad (M)
import qualified Hix.Data.Options
import Hix.Data.Options (ProjectOptions)
import Hix.Data.PackageName (PackageName)
import Hix.Managed.Cabal.Config (cabalConfig)
import Hix.Managed.Cabal.Data.Config (CabalConfig)
import Hix.Managed.Data.BuildConfig (BuildConfig)
import qualified Hix.Managed.Data.EnvContext
import Hix.Managed.Data.EnvContext (EnvContext, EnvDeps)
import Hix.Managed.Data.Envs (Envs)
import Hix.Managed.Data.ManagedPackage (ManagedPackage (..), ProjectPackages)
import qualified Hix.Managed.Data.ProjectContext
import Hix.Managed.Data.ProjectContext (ProjectContext (ProjectContext))
import qualified Hix.Managed.Data.ProjectContextProto
import Hix.Managed.Data.ProjectContextProto (ProjectContextProto)
import Hix.Managed.Data.ProjectState (ProjectState)
import Hix.Managed.Data.Query (RawQuery (RawQuery))
import Hix.Managed.EnvContext (envContexts)
import qualified Hix.Managed.ProjectStateProto as ProjectStateProto
import Hix.Monad (fromEither, noteClient)

validateQuery ::
  Set PackageName ->
  RawQuery ->
  Either ErrorMessage (Maybe (NonEmpty PackageName))
validateQuery projectDeps (RawQuery query) =
  maybeToLeft (nonEmpty query) (err <$> find invalid query)
  where
    err :: PackageName -> ErrorMessage
    err dep = Client [exon|'##{dep}' is not a dependency of any package.|]

    invalid dep = not (Set.member dep projectDeps)

noEnvs :: Text
noEnvs =
  [exon|The flake config contains no managed envs.
Most likely this means that you ran the CLI directly.
Please use one of the flake apps '.#bump', .#lower.init', '.#lower.optimize' or '.#lower.stabilize'.|]

unknownEnv :: EnvName -> Text
unknownEnv name =
  [exon|You requested to update the env '##{name}', but it is not present in the managed deps config.
Maybe this env is not enabled for managed dependencies.|]

selectEnvs ::
  Envs (Either EnvDeps EnvContext) ->
  [EnvName] ->
  M (NonEmpty (Either EnvName EnvContext))
selectEnvs envs specified =
  traverse valid =<< noteClient noEnvs (nonEmpty specified <|> nonEmpty (nKeys envs))
  where
    valid env = noteClient (unknownEnv env) (first (const env) <$> envs !? env)

projectContext ::
  BuildConfig ->
  ProjectState ->
  ProjectPackages ->
  NonEmpty (Either EnvName EnvContext) ->
  CabalConfig ->
  ProjectContext
projectContext build state packages envs cabal =
  ProjectContext {..}

validate ::
  ProjectOptions ->
  ProjectContextProto ->
  M ProjectContext
validate opts proto = do
  query <- fromEither (validateQuery projectDeps opts.query)
  let contexts = envContexts opts proto.packages proto.envs query
      envDeps = nMap (either id (.deps)) contexts
  state <- ProjectStateProto.validateProjectState opts ranges envDeps proto.state
  envTargets <- selectEnvs contexts opts.envs
  cabal <- cabalConfig proto.hackage opts.cabal
  pure ProjectContext {
    build = opts.build,
    state,
    packages = proto.packages,
    envs = envTargets,
    cabal
  }
  where
    projectDeps = nConcat ranges (const nKeysSet)

    ranges =
      nOver proto.packages \ ManagedPackage {deps} ->
        nGen deps \ Dep {package = depPackage, ..} ->
          (depPackage, version)