cabal-debian-3.0.3: src/Debian/Debianize/Generic.hs
-- | Some generic operations with specializations to avoid broken Data
-- instances in types like Text and Set.
{-# LANGUAGE DeriveDataTypeable, RankNTypes, StandaloneDeriving #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Debian.Debianize.Generic
( geq
, gdiff
, gshow
) where
import Prelude hiding (GT)
import Data.Generics (Data, Typeable, GenericQ, toConstr, showConstr, gzipWithQ, extQ, ext1Q, gmapQ, Constr {- , ext2Q, Typeable2, dataTypeName, dataTypeOf-})
import Data.List (sort)
import qualified Data.Text as T
import Data.Set as Set (Set, toList, fromList, difference)
import Debian.Debianize.Atoms (Atoms)
import Debian.Debianize.ControlFile (VersionControlSpec, XField)
import Debian.Debianize.Utility (showDeps)
import Debian.Relation (Relation)
import Triplets (mkQ2, extQ2)
-- These instances are only used here, to create debugging messages.
deriving instance Typeable Atoms
-- deriving instance Data Debianization
-- deriving instance Data DebAtom
-- ext2Q' :: (Data d, Typeable2 t) => (d -> q) -> (forall d1 d2. (Data d1, Data d2) => t d1 d2 -> q) -> d -> q
-- ext2Q' = ext2Q
geq :: GenericQ (GenericQ Bool)
geq x y =
(geq' `mkQ2` stringEq `extQ2` textEq `extQ2` setEq1 `extQ2` setEq2 `extQ2` mapEq1) x y
where
-- If the specialized eqs don't work, use the generic. This
-- will throw an exception if it encounters something with a
-- NoRep type.
geq' :: (Data a, Data b) => a -> b -> Bool
geq' x' y' = (toConstr x' == toConstr y') && and (gzipWithQ geq x' y')
stringEq :: String -> String -> Bool
stringEq a b = (a == b)
textEq :: T.Text -> T.Text -> Bool
textEq a b = (a == b)
setEq1 :: Set VersionControlSpec -> Set VersionControlSpec -> Bool
setEq1 a b = toList a == toList b
setEq2 :: Set XField -> Set XField -> Bool
setEq2 a b = toList a == toList b
mapEq1 :: Atoms -> Atoms -> Bool
mapEq1 a b = (a == b)
data Diff
= Diff { stack :: [Constr], expected :: String, actual :: String }
| SetDiff { stack :: [Constr], expected :: String, missing :: String, extra :: String }
deriving (Eq, Show)
gdiff :: GenericQ (GenericQ [Diff])
gdiff x y =
(gdiff' `mkQ2` stringEq `extQ2` textEq `extQ2` setEq1 `extQ2` setEq2 `extQ2` mapEq1 `extQ2` relEq) x y
where
-- If the specialized eqs don't work, use the generic. This
-- will throw an exception if it encounters something with a
-- NoRep type.
gdiff' :: (Data a, Data b) => a -> b -> [Diff]
gdiff' x' y' =
if toConstr x' == toConstr y'
then map (\ diff -> diff {stack = toConstr x' : stack diff}) (concat (gzipWithQ gdiff x' y'))
else [Diff {stack = [], expected = gshow x', actual = gshow y'}]
stringEq :: String -> String -> [Diff]
stringEq a b = if (a == b) then [] else [Diff {stack = [], expected = show a, actual = show b}]
textEq :: T.Text -> T.Text -> [Diff]
textEq a b = if a == b then [] else [Diff {stack = [], expected = show a, actual = show b}]
setEq1 :: Set VersionControlSpec -> Set VersionControlSpec -> [Diff]
setEq1 a b = if a == b then [] else [Diff {stack = [], expected = show a, actual = show b}]
setEq2 :: Set XField -> Set XField -> [Diff]
setEq2 a b = if a == b then [] else [Diff {stack = [], expected = show a, actual = show b}]
mapEq1 :: Atoms -> Atoms -> [Diff]
mapEq1 a b = if a == b then [] else [Diff {stack = [], expected = show a, actual = show b}]
relEq :: [[Relation]] -> [[Relation]] -> [Diff]
relEq a b = if Set.fromList a == Set.fromList b
then []
else [SetDiff {stack = [],
expected = show [a, b],
missing = showDeps (sort (Set.toList (Set.difference (Set.fromList a) (Set.fromList b)))),
extra = showDeps (sort (Set.toList (Set.difference (Set.fromList b) (Set.fromList a))))}]
{-
gshow' :: Data a => a -> String
gshow' x =
(gshow `extQ` (show :: T.Text -> String) `extQ` (show :: Maybe T.Text -> String)) x
-}
-- | Generic show: an alternative to \"deriving Show\"
gshow :: Data a => a -> String
gshow x = gshows x ""
-- | Generic shows
gshows :: Data a => a -> ShowS
-- This is a prefix-show using surrounding "(" and ")",
-- where we recurse into subterms with gmapQ.
gshows = ( \t ->
showChar '('
. (showString . showConstr . toConstr $ t)
. (foldr (.) id . gmapQ ((showChar ' ' .) . gshows) $ t)
. showChar ')'
) `extQ` (shows :: String -> ShowS)
`extQ` ((shows . T.unpack) :: T.Text -> ShowS)
`ext1Q` (\ s -> gshows (toList s))