    {- 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 a. [a] -> 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 a. [a] -> 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