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