{- text based table generation
 -
 - Copyright 2014 Joey Hess <id@joeyh.name>
 -
 - License: BSD-2-clause
 -}

module Utility.Table where

type Table = [[String]]

-- | A table with a header that is set off with lines under each
-- header item.
tableWithHeader :: [String] -> [[String]] -> Table
tableWithHeader :: [String] -> [[String]] -> [[String]]
tableWithHeader header :: [String]
header rows :: [[String]]
rows = [String]
header [String] -> [[String]] -> [[String]]
forall a. a -> [a] -> [a]
: (String -> String) -> [String] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map String -> String
forall b. [b] -> String
linesep [String]
header [String] -> [[String]] -> [[String]]
forall a. a -> [a] -> [a]
: [[String]]
rows
  where
	linesep :: [b] -> String
linesep = (b -> Char) -> [b] -> String
forall a b. (a -> b) -> [a] -> [b]
map (Char -> b -> Char
forall a b. a -> b -> a
const '-')

-- | Formats a table to lines, automatically padding columns to the same size.
formatTable :: Table -> [String]
formatTable :: [[String]] -> [String]
formatTable table :: [[String]]
table = ([String] -> String) -> [[String]] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (\r :: [String]
r -> [String] -> String
unwords (((String, Int) -> String) -> [(String, Int)] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (String, Int) -> String
pad ([String] -> [Int] -> [(String, Int)]
forall a b. [a] -> [b] -> [(a, b)]
zip [String]
r [Int]
colsizes))) [[String]]
table
  where
	pad :: (String, Int) -> String
pad (cell :: String
cell, size :: Int
size) = String
cell String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String -> String
forall a. Int -> [a] -> [a]
take (Int
size Int -> Int -> Int
forall a. Num a => a -> a -> a
- String -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
cell) String
padding
	padding :: String
padding = Char -> String
forall a. a -> [a]
repeat ' '
	colsizes :: [Int]
colsizes = [Int] -> [Int]
forall a. [a] -> [a]
reverse ([Int] -> [Int]) -> [Int] -> [Int]
forall a b. (a -> b) -> a -> b
$ (0Int -> [Int] -> [Int]
forall a. a -> [a] -> [a]
:) ([Int] -> [Int]) -> [Int] -> [Int]
forall a b. (a -> b) -> a -> b
$ Int -> [Int] -> [Int]
forall a. Int -> [a] -> [a]
drop 1 ([Int] -> [Int]) -> [Int] -> [Int]
forall a b. (a -> b) -> a -> b
$ [Int] -> [Int]
forall a. [a] -> [a]
reverse ([Int] -> [Int]) -> [Int] -> [Int]
forall a b. (a -> b) -> a -> b
$
		[[Int]] -> [Int]
forall c. (Num c, Ord c) => [[c]] -> [c]
sumcols (([String] -> [Int]) -> [[String]] -> [[Int]]
forall a b. (a -> b) -> [a] -> [b]
map ((String -> Int) -> [String] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map String -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length) [[String]]
table)
	sumcols :: [[c]] -> [c]
sumcols [] = c -> [c]
forall a. a -> [a]
repeat 0
	sumcols [r :: [c]
r] = [c]
r
	sumcols (r1 :: [c]
r1:r2 :: [c]
r2:rs :: [[c]]
rs) = [[c]] -> [c]
sumcols ([[c]] -> [c]) -> [[c]] -> [c]
forall a b. (a -> b) -> a -> b
$ (c -> c -> c) -> [c] -> [c] -> [c]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith c -> c -> c
forall a. Ord a => a -> a -> a
max [c]
r1 [c]
r2 [c] -> [[c]] -> [[c]]
forall a. a -> [a] -> [a]
: [[c]]
rs