nix-diff-1.0.21: src/Nix/Diff/Transformations.hs
module Nix.Diff.Transformations where
import qualified Patience
import Data.Generics.Uniplate.Data ( transformBi )
import Nix.Diff.Types
import qualified Data.Set as Set
import Data.Text (Text)
{-| 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 OnlyAlreadyComparedBelow.
-}
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
{-| If packages deep in the dependency graph have been changed, many other
derivations will also change in an uninteresting manner. This can lead to
hundreds or thousands of lines of output like this:
```
• The input derivation named `bash-5.2p32` differs
• These two derivations have already been compared
• The input derivation named `ensure-newer-sources-hook` differs
• These two derivations have already been compared
• The input derivation named `pypa-install-hook` differs
• These two derivations have already been compared
```
This transformation will fold sequences of `OneDerivationDiff` like this
into a single `ManyDerivationsAlreadyComparedDiff`.
-}
foldManyInputDerivationsAlreadyCompared :: DerivationDiff -> DerivationDiff
foldManyInputDerivationsAlreadyCompared dd = case dd of
DerivationsAreTheSame -> dd
AlreadyCompared -> dd
OnlyAlreadyComparedBelow{} -> dd
NamesDontMatch{} -> dd
OutputsDontMatch{} -> dd
DerivationDiff{..} ->
let inputsDiff' = transformNestedDerivationDiffs
foldManyInputDerivationsAlreadyCompared
inputsDiff
helper :: [Text] -> [InputDerivationsDiff] -> [InputDerivationsDiff]
helper [] [] = []
helper names [] =
[ManyDerivationsAlreadyComparedDiff
{ drvNames = Set.fromList names
}]
helper names (input:inputs) =
case input of
OneDerivationDiff
{ drvName
, drvDiff = AlreadyCompared
} -> helper
(drvName:names)
inputs
OneDerivationDiff{} -> input : helper names inputs
SomeDerivationsDiff{} -> input : helper names inputs
ManyDerivationsAlreadyComparedDiff{} -> error "unreachable"
in DerivationDiff
{ inputsDiff =
inputsDiff'
{ inputDerivationDiffs =
helper [] inputsDiff'.inputDerivationDiffs
}
, ..
}
{-| 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
ManyDerivationsAlreadyComparedDiff {} -> 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