{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
module Text.PrettyPrint.Records (FQuery, VQuery, RFmt, format, formatUntil, fields, values,
dfltFmt, simpleFmt, tableFmt, Format(..), TableFmt(..), formatTable) where
import GHC.Generics
import Data.Typeable (cast, Typeable)
import Text.PrettyPrint.Boxes ((<+>), vcat, text, Box(), left, hsep, top)
import Data.List (transpose)
class FQuery' f where
fields' :: f p -> [String]
instance FQuery' V1 where
fields' _ = []
instance FQuery' U1 where
fields' _ = []
instance FQuery c => FQuery' (Rec0 c) where
fields' _ = []
instance (FQuery' f, FQuery' g) => FQuery' (f :*: g) where
fields' (x :*: y) = fields' x <> fields' y
instance FQuery' f => FQuery' (D1 c f) where
fields' (M1 x) = fields' x
instance FQuery' f => FQuery' (C1 c f) where
fields' (M1 x) = fields' x
instance Selector s => FQuery' (S1 s f) where
fields' x = [selName x]
class FQuery a where
fields :: a -> [String]
default fields :: (Generic a, FQuery' (Rep a)) => a -> [String]
fields = fields' . from
class VQuery' f where
values' :: f p -> [String]
instance VQuery' f => VQuery' (M1 t c f) where
values' (M1 x) = values' x
instance (VQuery' f, VQuery' g) => VQuery' (f :*: g) where
values' (x :*: y) = values' x <> values' y
instance (Show c) => VQuery' (Rec0 c) where
values' K1{unK1=v} = [show v]
instance VQuery' U1 where
values' _ = []
instance VQuery' V1 where
values' _ = []
class VQuery a where
values :: a -> [String]
default values :: (Generic a, VQuery' (Rep a)) => a -> [String]
values = values' . from
data Format a = Format
{
arg :: String -> Box,
label :: String -> Box -> Box,
finally :: [Box] -> Box
}
class RFmt' f where
fvalues' :: (Typeable a, RFmt a) => Int -> f p -> Format a -> [Box]
class (Typeable a, FQuery a, Show a) => RFmt a where
fvalues :: Int -> a -> Format a -> [Box]
default fvalues :: (Generic a, RFmt' (Rep a)) => Int -> a -> Format a -> [Box]
fvalues 0 _ _ = [text "....."]
fvalues n a f = fvalues' n (from a) f
instance RFmt' f => RFmt' (M1 t c f) where
fvalues' n (M1 x) = fvalues' n x
instance (RFmt' f, RFmt' g) => RFmt' (f :*: g) where
fvalues' n (x :*: y) f = let
l = fvalues' n x f
r = fvalues' n y f
in l <> r
instance (Typeable c, Show c) => RFmt' (Rec0 c) where
fvalues' n K1{unK1=v} f = let
g :: Typeable k => Format k -> Maybe k
g _ = cast v
in case g f of
Nothing -> [arg f . show $ v]
Just x -> [formatUntil (n - 1) x f]
instance RFmt' U1 where
fvalues' _ _ _ = []
instance RFmt' V1 where
fvalues' _ _ _ = []
data TableFmt = TableFmt
{
argT :: String -> Box,
labelT :: String -> [Box] -> Box,
finallyT :: [Box] -> Box
}
dfltFmt :: Format a
dfltFmt = Format
{ arg = text
, label = \a b -> text (a <> ":") <+> b
, finally = vcat left }
tableFmt :: TableFmt
tableFmt = TableFmt
{ argT = text
, labelT = \a bs -> vcat left $ text a : bs
, finallyT = hsep 1 top }
simpleFmt :: (VQuery a, FQuery a) => (String -> String -> b) -> a -> [b]
simpleFmt f x = zipWith f (fields x) (values x)
formatUntil :: RFmt a => Int -> a -> Format a -> Box
formatUntil n a f = let
lhs = fields a
rhs = fvalues n a f
in if null rhs
then arg f . show $ a
else finally f $ zipWith (label f) lhs rhs
format :: RFmt a => a -> Format a -> Box
format = formatUntil (-1)
formatTable :: RFmt a => [a] -> TableFmt -> Box
formatTable as t = let
minFmt = Format (argT t) undefined (finallyT t)
header = fields (head as)
fvals = transpose $ flip (fvalues 1) minFmt <$> as
in finallyT t $ zipWith (labelT t) header fvals