module Text.Tabular( Table(..), mkTable, combine, unTable ) where
import Data.List( intercalate )
newtype Table a = Table [Row a]
newtype Row a = Row [Cell a]
data Cell a = Cell { cellWidth :: !Int
, cellData :: !a }
mkTable :: (Show a) => [[a]] -> Table a
mkTable rows = Table $ mkRows rows
where
widths = colWidths rows
mkRows rows = [ Row (map mkCell (zip widths row)) | row <- rows ]
mkCell = uncurry Cell
unTable :: Table a -> [[a]]
unTable (Table rows) = [ map cellData r | (Row r) <- rows ]
combine :: (Show a) => Table a -> Table a -> Table a
combine t t' = mkTable (unTable t ++ unTable t')
colWidths :: (Show a) => [[a]] -> [Int]
colWidths = map (maximum . map (length . show)) . zipn
instance (Show a) => Show (Table a) where
show (Table rows) = intercalate "\n" $ map showRow rows
where
showRow (Row cols) = intercalate " " $ colStrings
where
colStrings = [ padString (cellWidth c) (show d)
| c@(Cell {cellData=d}) <- cols ]
padString :: Int -> String -> String
padString maxWidth str = str ++ replicate padLen ' '
where padLen = maxWidth length str
zipn :: [[a]] -> [[a]]
zipn xss | any null xss = []
zipn xss = map head xss : zipn (map tail xss)