{- 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 [String]
header [[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 {a}. [a] -> String
linesep [String]
header [String] -> [[String]] -> [[String]]
forall a. a -> [a] -> [a]
: [[String]]
rows
  where
	linesep :: [a] -> String
linesep = (a -> Char) -> [a] -> String
forall a b. (a -> b) -> [a] -> [b]
map (Char -> a -> Char
forall a b. a -> b -> a
const Char
'-')

-- | Formats a table to lines, automatically padding columns to the same size.
formatTable :: Table -> [String]
formatTable :: [[String]] -> [String]
formatTable [[String]]
table = ([String] -> String) -> [[String]] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (\[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 (String
cell, 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 Char
' '
	colsizes :: [Int]
colsizes = [Int] -> [Int]
forall a. [a] -> [a]
reverse ([Int] -> [Int]) -> [Int] -> [Int]
forall a b. (a -> b) -> a -> b
$ (Int
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 Int
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 c
0
	sumcols [[c]
r] = [c]
r
	sumcols ([c]
r1:[c]
r2:[[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