{-# LANGUAGE DeriveDataTypeable #-}
module ReprTree (reprTree, reprTreeString) where
import Data.Tree
import Data.Generics
import Data.List
import Data.Maybe
import Data.Text (Text)
import qualified Data.Text as Text
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Set (Set)
import qualified Data.Set as Set
-- | A data representation in form of a formatted multiline string, such as
-- the following:
--
-- @
-- :
-- - A
-- | - :
-- | | - a
-- | | - b
-- | | - c
-- | - 9
-- - C
-- | - 3
-- - B
-- - D
-- - :
-- - :
-- | - asdf
-- | - 123
-- | - ldskfjkl
-- - :
-- - f
-- @
--
-- Which is a result of running the following code:
--
-- > import Data.Generics (Data, Typeable)
-- >
-- > data SomeType =
-- > A [String] Int |
-- > B |
-- > C Int |
-- > D [[String]]
-- > deriving (Typeable, Data)
-- >
-- > xxx = A ["a", "b", "c"] 9
-- > : C 3
-- > : B
-- > : D [["asdf", "123", "ldskfjkl"], ["f"]]
-- > : []
-- >
-- > main = putStrLn $ reprTreeString xxx
--
reprTreeString :: (Data a) => a -> String
reprTreeString = unlines . treeLines . reprTree where
treeLines (Node x ts) = x : subTreesLines ts
subTreesLines [] = []
subTreesLines [t] = shift "- " " " (treeLines t)
subTreesLines (t:ts) = shift "- " "| " (treeLines t) ++ subTreesLines ts
shift first other = zipWith (++) (first : repeat other)
-- | Get a representation tree of a generic data structure using SYB. Can be
-- used to implement a custom converter to textual representation.
reprTree :: Data a => a -> Tree String
reprTree = adtReprTree
`ext2Q` mapReprTree
`ext2Q` pairReprTree
`ext1Q` listReprTree
`ext1Q` setReprTree
`extQ` textReprTree
`extQ` stringReprTree
textReprTree :: Text -> Tree String
textReprTree x = Node (Text.unpack x) []
stringReprTree :: String -> Tree String
stringReprTree x = Node x []
adtReprTree :: Data a => a -> Tree String
adtReprTree a = Node (stripBraces $ showConstr $ toConstr a) (gmapQ reprTree a)
where
stripBraces :: String -> String
stripBraces s =
fromMaybe s $
stripPrefix "(" s >>= fmap reverse . stripPrefix ")" . reverse
mapReprTree :: (Data a, Data k) => Map k a -> Tree String
mapReprTree = Node "Map" . map pairReprTree . Map.toList
pairReprTree :: (Data a, Data b) => (a, b) -> Tree String
pairReprTree (a, b) = Node "," [reprTree a, reprTree b]
listReprTree :: (Data a) => [a] -> Tree String
listReprTree = Node ":" . map reprTree
setReprTree :: (Data a) => Set a -> Tree String
setReprTree = Node "Set" . map reprTree . Set.toList