pptable 0.1.0.1 → 0.2.0.0
raw patch · 8 files changed
+486/−329 lines, 8 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Text.PrettyPrint.PrettyTable: class (Data a) => Tabilize a where listToBox a = case a of { [] -> [[nullBox]] x : xs -> gprintTable (from x) : listToBox xs } mapToBox m = elems (mapWithKey (\ k v -> text (show k) : gprintTable (from v)) m) vectorToBox v = toList $ fmap (\ x -> (gprintTable (from x))) v printMap m = do { let r = head . elems $ m; let header = constrFields . toConstr $ r; let header_box = "Key" : map (text) header; printBox $ alignBox $ header_box : mapToBox m } printList m = do { let r = head $ m; let header = constrFields . toConstr $ r; let header_box = map (text) header; printBox $ alignBox $ header_box : listToBox m } printVector m = do { let r = m ! 0; let header = constrFields . toConstr $ r; let header_box = map (text) header; printBox $ alignBox $ header_box : vectorToBox m }
- Text.PrettyPrint.PrettyTable: listToBox :: (Tabilize a, Generic a, GTabilize (Rep a)) => [a] -> [[Box]]
- Text.PrettyPrint.PrettyTable: mapToBox :: (Tabilize a, Generic a, GTabilize (Rep a), Show b) => Map b a -> [[Box]]
- Text.PrettyPrint.PrettyTable: printList :: Tabilize a => [a] -> IO ()
- Text.PrettyPrint.PrettyTable: printMap :: (Tabilize a, Show b) => Map b a -> IO ()
- Text.PrettyPrint.PrettyTable: printVector :: Tabilize a => Vector a -> IO ()
- Text.PrettyPrint.PrettyTable: vectorToBox :: (Tabilize a, Generic a, GTabilize (Rep a)) => Vector a -> [[Box]]
- Text.PrettyPrint.Tabilize: class (Data a) => Tabilize a where listToBox a = case a of { [] -> [[nullBox]] x : xs -> gprintTable (from x) : listToBox xs } mapToBox m = elems (mapWithKey (\ k v -> text (show k) : gprintTable (from v)) m) vectorToBox v = toList $ fmap (\ x -> (gprintTable (from x))) v printMap m = do { let r = head . elems $ m; let header = constrFields . toConstr $ r; let header_box = "Key" : map (text) header; printBox $ alignBox $ header_box : mapToBox m } printList m = do { let r = head $ m; let header = constrFields . toConstr $ r; let header_box = map (text) header; printBox $ alignBox $ header_box : listToBox m } printVector m = do { let r = m ! 0; let header = constrFields . toConstr $ r; let header_box = map (text) header; printBox $ alignBox $ header_box : vectorToBox m }
- Text.PrettyPrint.Tabilize: instance (Data.Data.Data a, GHC.Show.Show a) => Text.PrettyPrint.Tabilize.GTabilize (GHC.Generics.K1 i a)
- Text.PrettyPrint.Tabilize: instance (Text.PrettyPrint.Tabilize.GTabilize a, Text.PrettyPrint.Tabilize.GTabilize b) => Text.PrettyPrint.Tabilize.GTabilize (a GHC.Generics.:*: b)
- Text.PrettyPrint.Tabilize: instance (Text.PrettyPrint.Tabilize.GTabilize a, Text.PrettyPrint.Tabilize.GTabilize b) => Text.PrettyPrint.Tabilize.GTabilize (a GHC.Generics.:+: b)
- Text.PrettyPrint.Tabilize: instance Text.PrettyPrint.Tabilize.GTabilize GHC.Generics.U1
- Text.PrettyPrint.Tabilize: instance Text.PrettyPrint.Tabilize.GTabilize a => Text.PrettyPrint.Tabilize.GTabilize (GHC.Generics.M1 i c a)
- Text.PrettyPrint.Tabilize: listToBox :: Tabilize a => [a] -> [[Box]]
- Text.PrettyPrint.Tabilize: mapToBox :: (Tabilize a, Show b) => Map b a -> [[Box]]
- Text.PrettyPrint.Tabilize: printList :: Tabilize a => [a] -> IO ()
- Text.PrettyPrint.Tabilize: printMap :: (Tabilize a, Show b) => Map b a -> IO ()
- Text.PrettyPrint.Tabilize: printVector :: Tabilize a => Vector a -> IO ()
- Text.PrettyPrint.Tabilize: vectorToBox :: Tabilize a => Vector a -> [[Box]]
+ Text.PrettyPrint.Tabulate: class Boxable b
+ Text.PrettyPrint.Tabulate: class CellValueFormatter a where ppFormatter x = show x ppFormatterWithStyle _ x = "default_" ++ show x
+ Text.PrettyPrint.Tabulate: class (Data a) => Tabulate a where ppTable x = printTable x
+ Text.PrettyPrint.Tabulate: instance (Text.PrettyPrint.Tabulate.GTabulate a, Text.PrettyPrint.Tabulate.GTabulate b) => Text.PrettyPrint.Tabulate.GTabulate (a GHC.Generics.:*: b)
+ Text.PrettyPrint.Tabulate: instance (Text.PrettyPrint.Tabulate.GTabulate a, Text.PrettyPrint.Tabulate.GTabulate b) => Text.PrettyPrint.Tabulate.GTabulate (a GHC.Generics.:+: b)
+ Text.PrettyPrint.Tabulate: instance GHC.Show.Show k => Text.PrettyPrint.Tabulate.Boxable (Data.Map.Base.Map k)
+ Text.PrettyPrint.Tabulate: instance Text.PrettyPrint.Tabulate.Boxable Data.Vector.Vector
+ Text.PrettyPrint.Tabulate: instance Text.PrettyPrint.Tabulate.Boxable []
+ Text.PrettyPrint.Tabulate: instance Text.PrettyPrint.Tabulate.CellValueFormatter GHC.Base.String
+ Text.PrettyPrint.Tabulate: instance Text.PrettyPrint.Tabulate.CellValueFormatter GHC.Integer.Type.Integer
+ Text.PrettyPrint.Tabulate: instance Text.PrettyPrint.Tabulate.CellValueFormatter GHC.Types.Bool
+ Text.PrettyPrint.Tabulate: instance Text.PrettyPrint.Tabulate.CellValueFormatter GHC.Types.Double
+ Text.PrettyPrint.Tabulate: instance Text.PrettyPrint.Tabulate.CellValueFormatter GHC.Types.Float
+ Text.PrettyPrint.Tabulate: instance Text.PrettyPrint.Tabulate.CellValueFormatter GHC.Types.Int
+ Text.PrettyPrint.Tabulate: instance Text.PrettyPrint.Tabulate.CellValueFormatter a => Text.PrettyPrint.Tabulate.GTabulate (GHC.Generics.K1 i a)
+ Text.PrettyPrint.Tabulate: instance Text.PrettyPrint.Tabulate.GTabulate GHC.Generics.U1
+ Text.PrettyPrint.Tabulate: instance Text.PrettyPrint.Tabulate.GTabulate a => Text.PrettyPrint.Tabulate.GTabulate (GHC.Generics.M1 i c a)
+ Text.PrettyPrint.Tabulate: ppTable :: (Tabulate a, Boxable f, Generic a, GTabulate (Rep a)) => f a -> IO ()
+ Text.PrettyPrint.Tabulate: printTable :: (Boxable b, Data a, Generic a, GTabulate (Rep a)) => b a -> IO ()
+ Text.PrettyPrint.Tabulate: toBox :: (Boxable b, Data a, Generic a, GTabulate (Rep a)) => b a -> [[Box]]
Files
- CHANGELOG.md +17/−0
- README.md +49/−23
- pptable.cabal +4/−4
- src/Text/PrettyPrint/PrettyTable.hs +0/−85
- src/Text/PrettyPrint/Tabilize.hs +0/−187
- src/Text/PrettyPrint/Tabulate.hs +222/−0
- src/Text/PrettyPrint/Tabulate/Example.hs +180/−0
- test/Spec.hs +14/−30
+ CHANGELOG.md view
@@ -0,0 +1,17 @@+0.2.0.0+-------++* This release contains *breaking* changes+* Name of Tabilize class changed to Tabulate+* printList, printMap and printVector all replaced with polymorphic method ppTable+* New class Boxable with default instances for List, Vector and Map+* Use prinf library to print basic types+* New class CellValueFormatter with default instances for basic type+* Boxable instance to extend to other Traversable instances+* CellValueFormatter class to extend formatting++0.1.0.1+-------++* Intial version+* Can print List, Map or Vector in tabular format
README.md view
@@ -1,41 +1,67 @@ # README # -PrettyTable : Print any list, vector or map as a well-formatted readable table.+Text.PrettyPrint.Tabulate : Print any list, vector or map as a well-formatted readable table. -### Text.PrettyPrint.PrettyTable ###+### Text.PrettyPrint.Tabulate ### * This module provides simple functions used to print values in tabular format-* Version 0.1.0+* Version 0.2.0.0 * Contributions and Bug Reports welcome. Please use the Github issue tracker. -### Examples ###+### Example ### ``` haskell- -- Printing a list of records- :set -XDeriveGeneric- :set -XDeriveDataTypeable - import qualified GHC.Generics as G- import Data.Data+ -- ./Example.hs+ :set -XDeriveGeneric+ :set -XDeriveDataTypeable - -- A record structure that will in a list- data Stock = Stock {ticker::String, price::Double, marketCap::Double} deriving (Data, G.Generic)+ import qualified GHC.Generics as G+ import Data.Data - -- Create an instance of Tabilize- instance Tabilize Stock+ import qualified Text.PrettyPrint.Tabulate as T+ import qualified Data.Map as Map+ import qualified Data.List as List+ import qualified Data.Vector as Vector - let yahoo = Stock {ticker="YHOO", price=42.29101010, marketCap=40e9}- let google = Stock {ticker="GOOG", price=774.210101, marketCap=532.09e9}- let amazon = Stock {ticker="AMZN", price=799.161717, marketCap=378.86e9} - -- List of records- let tickers = [yahoo, google, amazon]+ data Stock = Stock {ticker::String, price::Double, marketCap::Double} deriving (Data, G.Generic)+ instance T.Tabulate Stock - printList tickers+ let yahoo = Stock {ticker="YHOO", price=42.29101010, marketCap=40e9}+ let google = Stock {ticker="GOOG", price=774.210101, marketCap=532.09e9}+ let amazon = Stock {ticker="AMZN", price=799.161717, marketCap=378.86e9} - ticker price marketCap- "YHOO" 42.2910101 4.0e10- "GOOG" 774.210101 5.3209e11- "AMZN" 799.161717 3.7886e11++ -- List of records+ let tickers = [yahoo, google, amazon]++ -- The record type 'Stock' can also be in a Map+ let tickers_map = Map.fromList [(10, yahoo), (100, google), (1000, amazon)]++ -- Or in a Vector+ let tickers_vector = Vector.fromList tickers++ -- Print table from List+ T.ppTable tickers+ ticker price marketCap+ YHOO 42.291010100 4.000000000e10+ GOOG 774.210101000 5.320900000e11+ AMZN 799.161717000 3.788600000e11++ -- Print table from Map+ T.ppTable tickers_map+ Key ticker price marketCap+ 10 YHOO 42.291010100 4.000000000e10+ 100 GOOG 774.210101000 5.320900000e11+ 1000 AMZN 799.161717000 3.788600000e11+++ -- Print table from Vector+ T.ppTable tickers_vector+ ticker price marketCap+ YHOO 42.291010100 4.000000000e10+ GOOG 774.210101000 5.320900000e11+ AMZN 799.161717000 3.788600000e11 ```
pptable.cabal view
@@ -1,5 +1,5 @@ name: pptable-version: 0.1.0.1+version: 0.2.0.0 synopsis: Pretty Print containers in a tabular format description: Please see README.md homepage: https://github.com/gdevanla/pptable#readme@@ -10,12 +10,12 @@ copyright: 2016 Guru Devanla category: Text build-type: Simple-extra-source-files: README.md+extra-source-files: README.md, CHANGELOG.md cabal-version: >=1.10 library hs-source-dirs: src- exposed-modules: Text.PrettyPrint.Tabilize, Text.PrettyPrint.PrettyTable+ exposed-modules: Text.PrettyPrint.Tabulate, Text.PrettyPrint.Tabulate.Example build-depends: base >= 4.7 && < 5 , syb , containers@@ -44,4 +44,4 @@ source-repository head type: git- location: https://github.com/githubuser/pptable+ location: https://github.com/gdevanla/pptable
− src/Text/PrettyPrint/PrettyTable.hs
@@ -1,85 +0,0 @@-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE Rank2Types #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE DefaultSignatures #-}-{-# LANGUAGE TypeOperators #-}---- | This module features methods that can be used to print values contained--- inside a list \/ a map \/ or a vector--- in a tabular format.--module Text.PrettyPrint.PrettyTable- (- -- ** Example of output- -- $outputexample-- -- ** Basic Steps- -- $steps- - -- ** Usage Example- -- $examplelist- - -- ** Tabilize methods- Tabilize(..)-- -- ** Custom Rendering- -- $return_box- - ) where--import Text.PrettyPrint.Tabilize----- $outputexample--- > ticker price marketCap--- > "YHOO" 42.2910101 4.0e10--- > "GOOG" 774.210101 5.3209e11--- > "AMZN" 799.161717 3.7886e11---- $steps------ * Declare the new data type that will be the element of a list, map or a vector.--- * The type could be a basic data constructor or a record type--- * Derive 'Generic' and 'Data' for the type--- * Make the type an instance of 'Text.PrettyPrint.Tabilize.Tabilize'--- * Create values of the type and add them to a list, map or vector--- * Use one of the 'printList', 'printMap' and 'printVector' methods to print the values in a tabular format---- $notes--- Note that any nested algebraic instances will be displayed by calling their respective 'Show' methods---- $examplelist------ > -- Printing a list of records--- > :set -XDeriveGeneric--- > :set -XDeriveDataTypeable--- >--- > import qualified GHC.Generics as G--- > import Data.Data--- >--- > -- A record structure that will in a list--- > data Stock = Stock {ticker::String, price::Double, marketCap::Double} deriving (Data, G.Generic)--- >--- > -- Create an instance of Tabilize--- > instance Tabilize Stock--- > --- > let yahoo = Stock {ticker="YHOO", price=42.29101010, marketCap=40e9}--- > let google = Stock {ticker="GOOG", price=774.210101, marketCap=532.09e9}--- > let amazon = Stock {ticker="AMZN", price=799.161717, marketCap=378.86e9}--- >--- > -- List of records--- > let tickers = [yahoo, google, amazon]--- > --- > printList tickers--- > --- > ticker price marketCap--- > "YHOO" 42.2910101 4.0e10--- > "GOOG" 774.210101 5.3209e11--- > "AMZN" 799.161717 3.7886e11----- $return_box--- In cases, where the 'printList', 'printMap' or 'printVector' statements are not adequate, the 'listToBox', 'vectorToBox' and 'mapToBox' methods--- return the B.Box value. The user can then use some of the methods provided by--- "Text.PrettyPrint.Box" module.
− src/Text/PrettyPrint/Tabilize.hs
@@ -1,187 +0,0 @@-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE Rank2Types #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE DefaultSignatures #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE ConstrainedClassMethods #-}---- | Module implements the default methods for Tabilize--module Text.PrettyPrint.Tabilize- (- - Tabilize- , printList- , printMap- , printVector- - , listToBox- , mapToBox- , vectorToBox-- )where--import Data.Data-import Data.Typeable-import Data.Generics.Aliases-import GHC.Generics as G-import GHC.Show-import qualified Data.Map as Map-import qualified Text.PrettyPrint.Boxes as B-import qualified Data.List as List-import Text.Printf-import qualified Data.Vector as V------ | The Generalized class that provides the print--- | functionality for any type that derives Generics and Data-class GTabilize f where- gprintTable :: f a -> [B.Box]----- | The instance class for Unit type-instance GTabilize U1 where- gprintTable _ = []---- | Any records or product types--- | Nested algebraic types are printed using their respective Show methods-instance (GTabilize a, GTabilize b) => GTabilize (a :*: b) where- gprintTable (a :*: b) = gprintTable a ++ gprintTable b---- | Sum types-instance (GTabilize a, GTabilize b) => GTabilize (a :+: b) where- gprintTable (L1 x) = gprintTable x- gprintTable (R1 x) = gprintTable x---- | -instance (GTabilize a) => GTabilize (M1 i c a) where- gprintTable (M1 x) = gprintTable x----- | The leaf method that actually creates the Boxes that will--- | be used to render the boxes as a table.-instance (Data a, Show a) => GTabilize (K1 i a) where- gprintTable (K1 x) = createBox x---- | Create the B.Box around the provided value--- | If the value is an algebraic type the algebraic types--- | Show method is used. All values have to be an instance of--- | Data. This dependency is there to support identify Numeric--- | values for right-aligning those values-createBox :: (Data x, Show x) => x -> [B.Box]-createBox x- | isBool = [B.text $ show x]- | isNumeric = [B.text $ printf "%10s" . show $ x]- | otherwise = [B.text $ show x]- where- isBool = get_type == "Bool"- isNumeric = get_type == "Integer" || get_type == "Double" || get_type == "Float"- get_type = tyConName . typeRepTyCon . typeOf $ x---- | Perform the final alignment of all--- | generated [[B.Box]]-alignBox :: [[B.Box]] -> B.Box-alignBox b = B.hsep 5 B.left cols- where- cols = List.map (B.vcat B.left) $ List.transpose b---- | Helper method that detects an algebraic data type-isAlgRepConstr t = case dataTypeRep . dataTypeOf $ t of- AlgRep [_] -> True- _ -> False---- | Specialized class that provides default methods--- methods to print List, Map or Vector values as a--- pretty table-class (Data a) => Tabilize a where- -- | Return a list of values wrapped in a Box. Each entry in input is assumed to be a- -- list of records keyed by any data type. The first entry in the- -- list of values will be used to infer the names of fields- listToBox :: [a] -> [[B.Box]]-- -- | Return a list of values wrapped in Box. Each entry in input is assumed to be a- -- list of records keyed by any data type. The first entry in the- -- list of values will be used to infer the names of fields- mapToBox ::(Show b) => Map.Map b a -> [[B.Box]]-- - -- | Return a list of values wrapped in Box. Each entry in input is assumed to be a- -- list of records keyed by any data type. The first entry in the- -- list of values will be used to infer the names of fields- vectorToBox :: V.Vector a -> [[B.Box]]-- default listToBox :: (G.Generic a, GTabilize (Rep a)) => [a] -> [[B.Box]]- listToBox a = case a of- [] -> [[B.nullBox]]- x:xs -> gprintTable (from x):listToBox xs-- - default mapToBox :: (G.Generic a, GTabilize (Rep a), Show b) => Map.Map b a -> [[B.Box]]- mapToBox m = Map.elems- (Map.mapWithKey- (\k v -> B.text (show k):gprintTable (from v))- m)-- default vectorToBox :: (G.Generic a, GTabilize (Rep a)) => V.Vector a -> [[B.Box]]- vectorToBox v = V.toList $ fmap (\x -> (gprintTable (from x))) v-- -- |- -- > import qualified Data.Map as M- -- > -- declare a Map- -- > data Portfolio = M.Map String Stock- -- > Add the Stock values we create- -- > let p = M.fromList [("YHOO", yahoo), ("GOOG", google), ("AMZN" amazon)]- -- >- -- > printMap p- -- >- -- > Key ticker price marketCap- -- > "amzn" "AMZN" 799.161717 3.7886e11- -- > "goog" "GOOG" 774.210101 5.3209e11- -- > "yhoo" "YHOO" 42.2910101 4.0e10- printMap :: (Show b) => Map.Map b a -> IO ()- printMap m = do- let r = head . Map.elems $ m- let header = constrFields . toConstr $ r- let header_box = "Key":List.map (B.text) header- B.printBox $ alignBox $ header_box:mapToBox m-- -- |- -- > -- List of records- -- > let tickers = [yahoo, google, amazon]- -- > - -- > printList tickers- -- > - -- > ticker price marketCap- -- > "YHOO" 42.2910101 4.0e10- -- > "GOOG" 774.210101 5.3209e11- -- > "AMZN" 799.161717 3.7886e11- printList :: [a] -> IO ()- printList m = do- let r = head $ m- let header = constrFields . toConstr $ r- let header_box = List.map (B.text) header- B.printBox $ alignBox $ header_box:listToBox m-- -- |- -- > import qualified Data.Vector as V- -- > -- Vector of records- -- > let tickers = V.fromList [yahoo, google, amazon]- -- > - -- > printVector tickers- -- > - -- > ticker price marketCap- -- > "YHOO" 42.2910101 4.0e10- -- > "GOOG" 774.210101 5.3209e11- -- > "AMZN" 799.161717 3.7886e11- printVector :: V.Vector a -> IO ()- printVector m = do- let r = m V.! 0- let header = constrFields . toConstr $ r- let header_box = List.map (B.text) header- B.printBox $ alignBox $ header_box:vectorToBox m-- -- print :: (Functor f) => f a -> f [B.Box]- -- default ppTabFL :: (G.Generic a, Tabilize (Rep a), Functor f) => (f a) -> f [B.Box]- -- ppTabFL f = fmap (\x -> (gprintTable (from x))) f-
+ src/Text/PrettyPrint/Tabulate.hs view
@@ -0,0 +1,222 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE DefaultSignatures #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE ConstrainedClassMethods #-}+{-# LANGUAGE MultiParamTypeClasses #-}++-- | Module implements the default methods for Tabulate++module Text.PrettyPrint.Tabulate+ (+ Tabulate(..)+ , Boxable(..)+ , CellValueFormatter+ + )where++import Data.Data+import Data.Typeable+import Data.Generics.Aliases+import GHC.Generics as G+import GHC.Show+import qualified Data.Map as Map+import qualified Text.PrettyPrint.Boxes as B+import qualified Data.List as List+import Text.Printf+import qualified Data.Vector as V++-- | Future change to support providing custom formatting functions+data TablizeValueFormat = T {floatValueFormat::Maybe (Float -> String),+ stringValueFormat::Maybe (String -> String),+ integerValueFormat::Maybe (Integer -> String),+ intValueFormat::Maybe (Int -> String),+ doubleValueFormat::Maybe (Double -> String)}++-- | Default TabulateValueFormat+getDefaultTabulateValueFormat = T {floatValueFormat=Nothing,+ stringValueFormat=Nothing,+ integerValueFormat=Nothing,+ intValueFormat=Nothing,+ doubleValueFormat=Nothing}+++-- | The Generalized class that implements the print feature+-- for any type that derives Generics and Data+class GTabulate f where+ -- | Print with default style+ gprintTable :: f a -> [B.Box]++ -- | For future, will be able to print with provided style+ gprintTableWithStyle :: TablizeValueFormat -> f a -> [B.Box]++--- | The instance class for Unit type+instance GTabulate U1 where+ gprintTable _ = []+ gprintTableWithStyle _ _ = []++-- | Any records or product types+-- | Nested algebraic types are printed using their respective Show methods+instance (GTabulate a, GTabulate b) => GTabulate (a :*: b) where+ gprintTable (a :*: b) = gprintTable a ++ gprintTable b+ gprintTableWithStyle style (a :*: b) = gprintTableWithStyle style a ++ gprintTableWithStyle style b++-- | Sum types+instance (GTabulate a, GTabulate b) => GTabulate (a :+: b) where+ gprintTable (L1 x) = gprintTable x+ gprintTable (R1 x) = gprintTable x++ gprintTableWithStyle style (L1 x) = gprintTableWithStyle style x+ gprintTableWithStyle style (R1 x) = gprintTableWithStyle style x++-- | +instance (GTabulate a) => GTabulate (M1 i c a) where+ gprintTable (M1 x) = gprintTable x+ gprintTableWithStyle style (M1 x) = gprintTableWithStyle style x+++-- | The leaf method that actually creates the Boxes that will+-- be used to render the boxes as a table.+instance (CellValueFormatter a) => GTabulate (K1 i a) where+ gprintTable (K1 x) = [B.text $ ppFormatter x]+ gprintTableWithStyle style (K1 x) = [B.text $ ppFormatterWithStyle style x]+++-- | Class that implements formatting using printf.+-- Default instances for String, Char, Int, Integer, Double and Float+-- are provided. For types that are not an instance of this class+-- `show` is used.+class CellValueFormatter a where++ -- Function that can be implemented by each instance+ ppFormatter :: a -> String++ -- Future support for this signature will be added+ ppFormatterWithStyle :: TablizeValueFormat -> a -> String++ -- Default instance of function for types that do+ -- do not have their own instance+ default ppFormatter :: (Show a) => a -> String+ ppFormatter x = show x++ -- Future support.+ default ppFormatterWithStyle :: (Show a) => TablizeValueFormat -> a -> String+ ppFormatterWithStyle _ x = "default_" ++ show x+++instance CellValueFormatter Integer where+ ppFormatter x = printf "%d" x+ + ppFormatterWithStyle style x = case integerValueFormat style of+ Just f -> f x+ Nothing -> ppFormatter x++instance CellValueFormatter Int where+ ppFormatter x = printf "%d" x++ ppFormatterWithStyle style x = case intValueFormat style of+ Just f -> f x+ Nothing -> ppFormatter x+++instance CellValueFormatter Float where+ ppFormatter x = printf "%14.9g" x++ ppFormatterWithStyle style x = case floatValueFormat style of+ Just f -> f x+ Nothing -> ppFormatter x++instance CellValueFormatter String where+ ppFormatter x = printf "%s" x++ ppFormatterWithStyle style x = case stringValueFormat style of+ Just f -> f x+ Nothing -> ppFormatter x+++instance CellValueFormatter Double where+ ppFormatter x = printf "%14.9g" x++ ppFormatterWithStyle style x = case doubleValueFormat style of+ Just f -> f x+ Nothing -> ppFormatter x++instance CellValueFormatter Bool++-- | Perform the final alignment of all+-- | generated [[B.Box]]+alignBox :: [[B.Box]] -> B.Box+alignBox b = B.hsep 5 B.left cols+ where+ cols = List.map (B.vcat B.left) $ List.transpose b++-- | Helper method that detects an algebraic data type+isAlgRepConstr t = case dataTypeRep . dataTypeOf $ t of+ AlgRep [_] -> True+ _ -> False++-- | Class that can be derived by a 'Traversable' to create+-- a list of 'Box' values and print as a Table.+-- Default instances for List, Map and Vector are already provided.+class Boxable b where+ toBox :: (Data a, G.Generic a, GTabulate(Rep a)) => b a -> [[B.Box]]+ --toBoxWithStyle :: (Data a, G.Generic a, GTabulate(Rep a)) => TablizeValueFormat -> b a -> [[B.Box]]+ + printTable :: (Data a, G.Generic a, GTabulate(Rep a)) => b a -> IO ()+ --printTableWithStyle :: (Data a, G.Generic a, GTabulate(Rep a)) => TablizeValueFormat -> b a -> IO ()++instance Boxable [] where+ toBox a = case a of+ [] -> [[B.nullBox]]+ x:xs -> gprintTable (from x):toBox xs++ -- | Prints a "List" as a table. Called by "ppTable"+ -- | Need not be called directly+ printTable m = do+ let r = head $ m+ let header = constrFields . toConstr $ r+ let header_box = List.map (B.text) header+ B.printBox $ alignBox $ header_box:toBox m++ +instance Boxable V.Vector where+ toBox v = V.toList $ fmap (\x -> (gprintTable (from x))) v++ -- | Prints a "Vector" as a table. Called by "ppTable"+ -- | Need not be called directly+ printTable m = do+ let r = m V.! 0+ let header = constrFields . toConstr $ r+ let header_box = List.map (B.text) header+ B.printBox $ alignBox $ header_box:toBox m++instance (Show k) => Boxable (Map.Map k) where+ -- | Returns a Map of Boxed values+ toBox m = Map.elems+ (Map.mapWithKey+ (\k v -> B.text (show k):gprintTable (from v))+ m)+ -- | Prints a "Map" as a table. Called by "ppTable"+ -- | Need not be called directly+ printTable m = do+ let r = head . Map.elems $ m+ let header = constrFields . toConstr $ r+ let header_box = "Key":List.map (B.text) header+ B.printBox $ alignBox $ header_box:toBox m++ +-- | The elements in a Traverserable should be an instance of Tabulate to be displayed in a tabular format+class (Data a) => Tabulate a where++ -- | Generic function that will be provided by the GTabulate class.+ ppTable :: (Boxable f) => f a -> IO()+ --ppTableWithStyle :: TablizeValueFormat -> f a -> IO()++ default ppTable :: (Boxable f, G.Generic a, GTabulate (Rep a)) => f a -> IO ()+ ppTable x = printTable x++ --default ppTableWithStyle :: (Boxable f, G.Generic a, GTabulate (Rep a)) => TablizeValueFormat -> f a -> IO ()+ --ppTableWithStyle x = printTableWithStyle x+
+ src/Text/PrettyPrint/Tabulate/Example.hs view
@@ -0,0 +1,180 @@+-- | Example usage of ppTable library+++module Text.PrettyPrint.Tabulate.Example+ (++ -- * Setup extensions+ -- $setup_extensions+ + -- * Required imports+ -- $import_generics_and_data++ -- * Import 'Tabulate'+ -- $import_tabulate++ -- * Use any 'Traversable' + -- $import_containers++ -- * Declare an instance of Tabulate+ -- $declare_record++ -- * Sample Data+ -- $create_sample_data++ -- * Printing the table+ -- $print_table++ -- * Complete Example+ -- $complete_example++ -- * Output+ -- $output+ + -- * Extending+ -- ** Extending for any 'Traversable'+ -- $boxable_instance+ + -- ** Customizing formatting+ -- $cell_value_formatter+ +++ ) where++import Text.PrettyPrint.Tabulate++-- $setup_extensions+-- > :set -XDeriveGeneric+-- > :set -XDeriveDataTypeable++-- $import_generics_and_data+-- Generics and Data need to be imported since+-- and type that will needs to be printed in tabular format+-- needs to derive from Generic and Data+--+-- > import qualified GHC.Generics as G+-- > import Data.Data+--++-- $import_tabulate+-- Tabulate class and ppTable function are the only declarations+-- to quickly using the library for printing in tabular format+--+-- > import qualified Text.PrettyPrint.Tabulate as T++-- $import_containers+-- The data type that we will print in tabular format will have+-- to be an element of a 'Traversable' instance. The ppTable library+-- provides default instances for 'Data.Map', 'Data.Vector' and 'Data.List'.+--+-- We import these modules here to provide examples of their use+-- +-- > import qualified Data.Map as Map+-- > import qualified Data.List as List+-- > import qualified Data.Vector as Vector+--++-- $declare_record+--+-- A record structure that will be an element in a 'Traversable' instance.+-- The data type has to derive from 'Generic' and 'Data'. +-- Also an default instance of 'Tabulate' is declared+--+-- > data Stock = Stock {ticker::String, price::Double, marketCap::Double} deriving (Data, G.Generic)+-- > instance T.Tabulate Stock+--++-- $create_sample_data+-- +-- > yahoo = Stock {ticker="YHOO", price=42.29101010, marketCap=40e9}+-- > google = Stock {ticker="GOOG", price=774.210101, marketCap=532.09e9}+-- > amazon = Stock {ticker="AMZN", price=799.161717, marketCap=378.86e9}+-- > -- List of records+-- > tickers = [yahoo, google, amazon]+-- >+-- > -- The record type 'Stock' can also be in a Map+-- > tickers_map = Map.fromList [(10, yahoo), (100, google), (1000, amazon)]+-- >+-- > -- Or in a Vector+-- > tickers_vector = Vector.fromList tickers++-- $print_table+-- +-- > putStrLn "\nElements in a List, with records fields as column names\n"+-- > T.ppTable tickers+-- >+-- > putStrLn "\nElement in a map, with Key as first column\n"+-- > T.ppTable tickers_map+-- >+-- > putStrLn "\nElement in a Vector\n"+-- > T.ppTable tickers_vector+-- + ++-- $complete_example+--+-- > -- ./Example.hs+-- > :set -XDeriveGeneric+-- > :set -XDeriveDataTypeable+-- >+-- > import qualified GHC.Generics as G+-- > import Data.Data+-- >+-- > import qualified Text.PrettyPrint.Tabulate as T+-- > import qualified Data.Map as Map+-- > import qualified Data.List as List+-- > import qualified Data.Vector as Vector+-- >+-- >+-- > data Stock = Stock {ticker::String, price::Double, marketCap::Double} deriving (Data, G.Generic)+-- > instance T.Tabulate Stock+-- >+-- > let yahoo = Stock {ticker="YHOO", price=42.29101010, marketCap=40e9}+-- > let google = Stock {ticker="GOOG", price=774.210101, marketCap=532.09e9}+-- > let amazon = Stock {ticker="AMZN", price=799.161717, marketCap=378.86e9}+-- >+-- >+-- > -- List of records+-- > let tickers = [yahoo, google, amazon]+--+-- > -- The record type 'Stock' can also be in a Map+-- > let tickers_map = Map.fromList [(10, yahoo), (100, google), (1000, amazon)]+-- >+-- > -- Or in a Vector+-- > let tickers_vector = Vector.fromList tickers++-- $output+-- > -- Print table from List+-- > T.ppTable tickers+-- > ticker price marketCap+-- > YHOO 42.291010100 4.000000000e10+-- > GOOG 774.210101000 5.320900000e11+-- > AMZN 799.161717000 3.788600000e11+-- >+-- > -- Print table from Map+-- >T.ppTable tickers_map+-- > Key ticker price marketCap+-- > 10 YHOO 42.291010100 4.000000000e10+-- > 100 GOOG 774.210101000 5.320900000e11+-- > 1000 AMZN 799.161717000 3.788600000e11+-- >+-- >+-- > -- Print table from Vector+-- > T.ppTable tickers_vector+-- > ticker price marketCap+-- > YHOO 42.291010100 4.000000000e10+-- > GOOG 774.210101000 5.320900000e11+-- > AMZN 799.161717000 3.788600000e11+-- >++++-- $boxable_instance+-- Any 'Traversable' containers can be extended to work with ppTable by implementing+-- an instance of 'Tabulate'++-- $cell_value_formatter+-- Default instances of 'CellValueFormatter' for 'Int', 'Integer', 'String', 'Float' and 'Double' are provided.+-- To customize formatting of any types, an specific implementation of 'CellValueFormatter' can be provided.+
test/Spec.hs view
@@ -3,11 +3,11 @@ {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE ConstrainedClassMethods #-} -import Test.Tasty+ import Data.Map as M import Data.List as L import Data.Vector as V-import Text.PrettyPrint.PrettyTable+import qualified Text.PrettyPrint.Tabulate as T import GHC.Generics as G import Data.Data import Text.PrettyPrint.Boxes as B@@ -23,45 +23,29 @@ test_float::Float, test_double::Double} deriving (Data, Show, G.Generic)-data R01 = R01 {r1_id::Int, nested_r::R0}- deriving (Show, G.Generic, Data)-data R3 = R3 {r3_id::Int, nested_rlist::[R0]}- deriving (Show, G.Generic, Data)-data R4 = R4 {r4_id::Int, nested_rtuple::(R0,R0)}- deriving (Show, G.Generic, Data)--instance Tabilize R0-instance Tabilize R01-instance Tabilize R3-instance Tabilize R4+-- data R01 = R01 {r1_id::Int, nested_r::R0}+-- deriving (Show, G.Generic, Data)+-- data R3 = R3 {r3_id::Int, nested_rlist::[R0]}+-- deriving (Show, G.Generic, Data)+-- data R4 = R4 {r4_id::Int, nested_rtuple::(R0,R0)}+-- deriving (Show, G.Generic, Data) +instance T.Tabulate R0+-- instance T.Tabulate R01+-- instance T.Tabulate R3+-- instance T.Tabulate R4 getR0 = R0 {test_string="Jack-somone" , test_integer=10 , test_double=10.101 , test_float=0.101021} --- nested record-getR1 = R01 {r1_id=1001, nested_r=getR0}---- nested record in list-getR3 = R3 {r3_id=1001, nested_rlist=[getR0, getR0]}--- tested nested record in tuple -getR4 = R4 {r4_id=1001, nested_rtuple=(getR0, getR0)}----testVector = testCase "testVector" (- -- do- -- create Vector- -- getBox- -- inspect Box (rows and columns)--- )- testList = testCase "testList" ( do- let b = (L.head . L.head) $ listToBox [getR0]+ let b = (L.head . L.head) $ T.toBox [getR0] assertEqual "check rows" (B.rows b) 1- assertEqual "check cols" (B.cols b) 13+ assertEqual "check cols" (B.cols b) 11 ) testLists = testGroup "test printing lists" [