packages feed

hix-0.9.0: lib/Hix/Data/Overrides.hs

module Hix.Data.Overrides where

import Data.Aeson (FromJSON (parseJSON), withObject, (.:), (.:?))
import Distribution.Pretty (Pretty (pretty))
import Distribution.Types.Version (Version)
import GHC.Exts (IsList)
import Text.PrettyPrint (brackets, hcat, text, (<+>))

import Hix.Class.EncodeNix (EncodeNix (..))
import Hix.Class.Map (LookupMaybe, NMap, nPretty)
import Hix.Data.Json (JsonParsec (JsonParsec))
import Hix.Data.NixExpr (Expr (ExprAttrs), ExprAttr (..), ExprKey)
import Hix.Data.PackageName (PackageName)
import Hix.Data.Version (SourceHash)
import Hix.Managed.Cabal.Data.HackageRepo (HackageName (..))
import Hix.Pretty (hpretty)

data IsRevision =
  IsRevision
  |
  IsNotRevision
  deriving stock (Eq, Show)

isRevision :: IsRevision -> Bool
isRevision = \case
  IsRevision -> True
  IsNotRevision -> False

toIsRevision :: Bool -> IsRevision
toIsRevision = \case
  True -> IsRevision
  False -> IsNotRevision

instance FromJSON IsRevision where
  parseJSON v =
    toIsRevision <$> parseJSON v

instance EncodeNix IsRevision where
  encodeNix = encodeNix . isRevision

data Override =
  Override {
    version :: Version,
    hash :: SourceHash,
    repo :: Maybe HackageName,
    revision :: Maybe IsRevision
  }
  |
  Jailbreak
  |
  Local
  deriving stock (Eq, Show, Generic)

instance EncodeNix Override where
  encodeNix = \case
    Override {..} ->
      ExprAttrs (static <> foldMap (pure . assoc "repo") repo <> foldMap (pure . assoc "revision") revision)
      where
        static = [assoc "version" version, assoc "hash" hash]

        assoc :: EncodeNix a => ExprKey -> a -> ExprAttr
        assoc name a = ExprAttr {name, value = encodeNix a}
    Jailbreak ->
      ExprAttrs [ExprAttr {name = "jailbreak", value = encodeNix True}]
    Local ->
      ExprAttrs [ExprAttr {name = "local", value = encodeNix True}]

override :: Version -> SourceHash -> Override
override version hash =
  Override {repo = Nothing, revision = Nothing, ..}

instance FromJSON Override where
  parseJSON =
    withObject "Override" \ o ->
      regular o <|> jailbreak o <|> local o
    where
      regular o = do
        JsonParsec version <- o .: "version"
        hash <- o .: "hash"
        repo <- o .:? "repo"
        revision <- o .:? "revision"
        pure Override {..}

      jailbreak o = do
        flag <- o .: "jailbreak"
        guard flag
        pure Jailbreak

      local o = do
        flag <- o .: "local"
        guard flag
        pure Local

instance Pretty Override where
  pretty = \case
    Override {..} ->
      pretty version <+> brackets (pretty hash <> foldMap renderRepo repo <> foldMap renderRevision revision)
    Jailbreak -> "jailbreak"
    Local -> "local"
    where
      renderRepo (HackageName name) =
        hcat [text ",", hpretty name]

      renderRevision = \case
        IsRevision -> ",rev"
        IsNotRevision -> mempty

-- | Overrides can be either for mutable (direct, nonlocal) deps, or for transitive deps, so they must use
-- 'PackageName'.
newtype Overrides =
  Overrides (Map PackageName Override)
  deriving stock (Eq, Show, Generic)
  deriving newtype (FromJSON, Semigroup, Monoid, IsList, EncodeNix)

instance NMap Overrides PackageName Override LookupMaybe where

instance Pretty Overrides where
  pretty = nPretty