packages feed

nix-diff-1.0.19: src/Nix/Diff/Transformations.hs

{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE FlexibleContexts #-}
module Nix.Diff.Transformations where

import qualified Patience
import Data.Generics.Uniplate.Data ( transformBi )

import Nix.Diff.Types

{-| In large diffs there may be a lot of derivations
    that doesn't change at all, but changed some of
    its nested inputs, that was already compared.
    This case will produce "stairs" of useless reports:
    ```
    • The input derivation named `a` differs
      - /nix/store/j1jmbxd74kzianaywml2nw1ja31a00r5-a.drv:{out}
      + /nix/store/ww51c2dha7m5l5qjzh2rblicsamkrh62-a.drv:{out}
      • The input derivation named `b` differs
        - /nix/store/j1jmbxd74kzianaywml2nw1ja31a00r5-b.drv:{out}
        + /nix/store/ww51c2dha7m5l5qjzh2rblicsamkrh62-b.drv:{out}
        • The input derivation named `c` differs
          • These two derivations have already been compared
    ```
    This transformation will fold all these subtrees of diff
    into one OnlyAlreadComparedBelow.
-}
foldAlreadyComparedSubTrees :: DerivationDiff -> DerivationDiff
foldAlreadyComparedSubTrees dd = case dd of
  DerivationsAreTheSame -> dd
  AlreadyCompared -> dd
  OnlyAlreadyComparedBelow{} -> dd
  NamesDontMatch{} -> dd
  OutputsDontMatch{} -> dd
  DerivationDiff{..} -> if
      | OutputsDiff Nothing [] <- outputsDiff
      , Nothing <- platformDiff
      , Nothing <- builderDiff
      , Nothing <- argumentsDiff
      , SourcesDiff Nothing [] <- sourcesDiff
      , InputsDiff Nothing inputs <- inputsDiff'
      , all alreadyComparedBelow inputs
      , envSkippedOrUnchanged envDiff
          -> OnlyAlreadyComparedBelow outputStructure

      | otherwise -> DerivationDiff
          { outputStructure
          , outputsDiff
          , platformDiff
          , builderDiff
          , argumentsDiff
          , sourcesDiff
          , inputsDiff = inputsDiff'
          , envDiff
          }
    where
      inputsDiff' = transformNestedDerivationDiffs
            foldAlreadyComparedSubTrees
            inputsDiff

{-| This transformation is most useful for
    --json output, because it will sqash a lot of
    `{"content":"  ","type":"Both"},{"content":"When","type":"Both"},{"content":" ","type":"Both"},{"content":"in","type":"Both"},{"content":" ","type":"Both"}`
    into one
    `{"content":"  When in ","type":"Both"}`
    block.

    To understand this problem clearer, see `golden-tests/expected-outputs/json`
    and `golden-tests/expected-outputs/json-squashed`.

    _Warning_: this transformation can break some parts of printing in
    human readable mode.
-}
squashSourcesAndEnvsDiff :: DerivationDiff -> DerivationDiff
squashSourcesAndEnvsDiff = transformBi
    \(TextDiff x) -> TextDiff (squashDiff x)
  where
    squashDiff (Patience.Old a : Patience.Old b : xs) =
      squashDiff (Patience.Old (a <> b) : xs)
    squashDiff (Patience.New a : Patience.New b : xs) =
      squashDiff (Patience.New (a <> b) : xs)
    squashDiff (Patience.Both a _ : Patience.Both b _ : xs) =
      let ab = a <> b in squashDiff (Patience.Both ab ab : xs)
    squashDiff (x : xs) = x : squashDiff xs
    squashDiff [] = []

-- ** Helpers

transformNestedDerivationDiffs
  :: (DerivationDiff -> DerivationDiff)
  -> InputsDiff
  -> InputsDiff
transformNestedDerivationDiffs f InputsDiff{..} = InputsDiff
  { inputExtraNames
  , inputDerivationDiffs = map changeDerivation inputDerivationDiffs
  }
  where
    changeDerivation idd = case idd of
      OneDerivationDiff name dd ->
        OneDerivationDiff name (f dd)
      SomeDerivationsDiff {} -> idd

envSkippedOrUnchanged :: Maybe EnvironmentDiff -> Bool
envSkippedOrUnchanged = \case
  Nothing -> True
  Just EnvironmentsAreEqual -> True
  _ -> False

alreadyComparedBelow :: InputDerivationsDiff -> Bool
alreadyComparedBelow = \case
  OneDerivationDiff _ AlreadyCompared -> True
  OneDerivationDiff _ OnlyAlreadyComparedBelow{} -> True
  _ -> False

transformIf :: Bool -> (DerivationDiff -> DerivationDiff) -> DerivationDiff -> DerivationDiff
transformIf False _ = id
transformIf True f = f