packages feed

hix-0.9.0: lib/Hix/Managed/Handlers/Context.hs

{-# language CPP #-}

#if __GLASGOW_HASKELL__ >= 908
{-# language TypeAbstractions #-}
#endif

module Hix.Managed.Handlers.Context where

import qualified Data.Aeson as Aeson
import Data.Aeson (FromJSON)
import Distribution.Pretty (Pretty (pretty))
import Exon (exon)
import Text.PrettyPrint (hang, text, (<+>))

import qualified Hix.Color as Color
import Hix.Data.Json (JsonConfig)
import Hix.Data.Monad (AppResources (..), M, appRes)
import Hix.Json (jsonConfig)
import qualified Hix.Log as Log
import Hix.Managed.Data.MaintContext (MaintContext)
import Hix.Managed.Data.ProjectContextProto (ProjectContextProto)
import Hix.Managed.Flake (runFlakeForSingleLine)
import Hix.Monad (appContext, eitherFatal, noteFatal, tryIOM)
import Hix.Pretty (HPretty (hpretty), showP)

type ContextKey :: Symbol -> Type -> Type
data ContextKey name a where
  ContextMaint :: ContextKey "maint" MaintContext
  ContextManaged :: ContextKey "managed" ProjectContextProto

instance KnownSymbol name => Pretty (ContextKey name a) where
  pretty _ = text (symbolVal (Proxy @name))

type ContextQuery :: Type -> Type
data ContextQuery a where
  ContextQuery :: ∀ name a . (FromJSON a, KnownSymbol name) => ContextKey name a -> ContextQuery a

contextKeySlug ::
  ∀ a b .
  IsString b =>
  ContextQuery a ->
  b
contextKeySlug (ContextQuery @name _) =
  fromString (symbolVal (Proxy @name))

instance Pretty (ContextQuery a) where
  pretty = text . contextKeySlug

data ContextHandlers =
  ContextHandlers {
    query :: ∀ a . ContextQuery a -> M a
  }

queryContextNoLog ::
  FromJSON a =>
  KnownSymbol name =>
  ContextHandlers ->
  ContextKey name a ->
  M a
queryContextNoLog ContextHandlers {query} key =
  query (ContextQuery key)

queryContext ::
  HPretty a =>
  FromJSON a =>
  KnownSymbol name =>
  ContextHandlers ->
  ContextKey name a ->
  M a
queryContext handlers key = do
  result <- queryContextNoLog handlers key
  Log.debugP $ hang (text "Context for" <+> pretty key) 2 (hpretty result)
  pure result

internalScope :: IsString a => a
internalScope = "__hix-internal__"

queryFlake :: ContextQuery a -> M a
queryFlake key@ContextQuery {} = do
  appContext [exon|querying the flake for the context #{Color.cyan name}|] do
    root <- appRes.root
    file <- decodeUtf8 <$> runFlakeForSingleLine desc root args id
    eitherFatal . first toText =<< tryIOM (Aeson.eitherDecodeFileStrict' file)
  where
    args = ["build", "--print-out-paths", [exon|.#{"#"}#{internalScope}.cli-context.json.#{name}|]]

    desc = [exon|Context query for '#{name}'|]

    name = contextKeySlug key

handlersProd :: ContextHandlers
handlersProd = ContextHandlers {query = queryFlake}

handlersTest :: (∀ a . ContextQuery a -> M (Maybe a)) -> ContextHandlers
handlersTest testContext =
  ContextHandlers {
    query = \ key -> check key =<< testContext key
  }
  where
    check key = noteFatal [exon|The context '#{showP key}' is not defined for this test.|]

jsonOrQuery ::
  FromJSON a =>
  KnownSymbol name =>
  ContextHandlers ->
  ContextKey name a ->
  Either a (Maybe JsonConfig) ->
  M a
jsonOrQuery ContextHandlers {query} confQuery =
  either pure (maybe (query (ContextQuery confQuery)) jsonConfig)

jsonOrQueryProd ::
  FromJSON a =>
  KnownSymbol name =>
  ContextKey name a ->
  Either a (Maybe JsonConfig) ->
  M a
jsonOrQueryProd =
  jsonOrQuery handlersProd