{-# language DefaultSignatures #-} {-# language FlexibleContexts #-} {-# language TypeOperators #-} module DhallToCabal.Diff ( Diffable(..) ) where import Data.List ( (\\), intersect ) import qualified Distribution.PackageDescription as Cabal import qualified Distribution.Types.ExecutableScope as Cabal import qualified Distribution.Types.ForeignLib as Cabal import qualified Distribution.Types.ForeignLibType as Cabal import qualified Distribution.Types.UnqualComponentName as Cabal import qualified GHC.Generics as Generic diffEqVia :: ( Monoid a, Eq b ) => ( a -> b ) -> a -> a -> ( b, b, b ) diffEqVia f left right = if f left == f right then ( f left, f mempty, f mempty ) else ( f mempty, f left, f right ) class Diffable a where diff :: a -> a -> ( a, a, a ) default diff :: ( Generic.Generic a, GDiffable ( Generic.Rep a ) ) => a -> a -> ( a, a, a ) diff a b = let ( common, left, right ) = gdiff ( Generic.from a ) ( Generic.from b ) in ( Generic.to common, Generic.to left, Generic.to right ) instance Diffable Cabal.BuildInfo where diff a b = let ( commonBuildable, leftBuildable, rightBuildable ) = diffEqVia Cabal.buildable a b ( common, left, right ) = case gdiff ( Generic.from a ) ( Generic.from b ) of ( common, left, right ) -> ( Generic.to common, Generic.to left, Generic.to right ) in ( common { Cabal.buildable = commonBuildable } , left { Cabal.buildable = leftBuildable } , right { Cabal.buildable = rightBuildable } ) instance Diffable Cabal.Library where diff a b = let ( commonLibExposed, leftLibExposed, rightLibExposed ) = diffEqVia Cabal.libExposed a b ( common, left, right ) = case gdiff ( Generic.from a ) ( Generic.from b ) of ( common, left, right ) -> ( Generic.to common, Generic.to left, Generic.to right ) in ( common { Cabal.libExposed = commonLibExposed } , left { Cabal.libExposed = leftLibExposed } , right { Cabal.libExposed = rightLibExposed } ) instance Diffable Cabal.Benchmark instance Diffable Cabal.TestSuite instance Diffable Cabal.Executable where diff a b = let ( commonModulePath, leftModulePath, rightModulePath ) = diffEqVia Cabal.modulePath a b ( common, left, right ) = case gdiff ( Generic.from a ) ( Generic.from b ) of ( common, left, right ) -> ( Generic.to common, Generic.to left, Generic.to right ) in ( common { Cabal.modulePath = commonModulePath } , left { Cabal.modulePath = leftModulePath } , right { Cabal.modulePath = rightModulePath } ) instance Diffable Cabal.ForeignLib instance Eq a => Diffable ( Maybe a ) where diff left right = if left == right then ( left, Nothing, Nothing ) else ( Nothing, left, right ) instance Diffable Cabal.UnqualComponentName where diff left right = if left == right then ( left, mempty, mempty ) else ( mempty, left, right ) instance Diffable Cabal.BenchmarkInterface where diff left right = if left == right then ( left, mempty, mempty ) else ( mempty, left, right ) instance Diffable Cabal.ForeignLibType where diff left right = if left == right then ( left, mempty, mempty ) else ( mempty, left, right ) instance Diffable Cabal.TestSuiteInterface where diff left right = if left == right then ( left, mempty, mempty ) else ( mempty, left, right ) instance Diffable Cabal.ExecutableScope where diff left right = if left == right then ( left, mempty, mempty ) else ( mempty, left, right ) instance Eq a => Diffable [a] where diff a b = ( intersect a b , a \\ b , b \\ a ) instance Diffable Bool where diff left right = if left == right then ( left, True, True ) else ( True, left, right ) class GDiffable f where gdiff :: f a -> f a -> ( f a, f a, f a ) instance GDiffable f => GDiffable ( Generic.M1 i c f ) where gdiff ( Generic.M1 a ) ( Generic.M1 b ) = let ( common, left, right ) = gdiff a b in ( Generic.M1 common, Generic.M1 left, Generic.M1 right ) instance ( GDiffable f, GDiffable g ) => GDiffable ( f Generic.:*: g ) where gdiff ( a Generic.:*: x ) ( b Generic.:*: y ) = let ( common0, left0, right0 ) = gdiff a b ( common1, left1, right1 ) = gdiff x y in ( common0 Generic.:*: common1, left0 Generic.:*: left1, right0 Generic.:*: right1 ) instance Diffable a => GDiffable ( Generic.K1 i a ) where gdiff ( Generic.K1 a ) ( Generic.K1 b ) = let ( common, left, right ) = diff a b in ( Generic.K1 common, Generic.K1 left, Generic.K1 right )