213 lines
4.9 KiB
Haskell
213 lines
4.9 KiB
Haskell
{- |
|
|
|
|
mtlstats
|
|
Copyright (C) 1984, 1985, 2019, 2020, 2021 Rhéal Lamothe
|
|
<rheal.lamothe@gmail.com>
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or (at
|
|
your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
-}
|
|
|
|
{-# LANGUAGE LambdaCase #-}
|
|
|
|
module Mtlstats.Format
|
|
( padNum
|
|
, left
|
|
, right
|
|
, centre
|
|
, padRight
|
|
, overlay
|
|
, month
|
|
, labelTable
|
|
, numTable
|
|
, tableWith
|
|
, complexTable
|
|
, overlayLast
|
|
, showFloating
|
|
) where
|
|
|
|
import Data.List (transpose)
|
|
|
|
import Mtlstats.Types
|
|
|
|
-- | Pad an 'Int' with leading zeroes to fit a certain character width
|
|
padNum
|
|
:: Int
|
|
-- ^ The width in characters
|
|
-> Int
|
|
-- ^ The value to format
|
|
-> String
|
|
padNum size n
|
|
| n < 0 = '-' : padNum (pred size) (-n)
|
|
| otherwise = let
|
|
str = show n
|
|
sLen = length str
|
|
pLen = size - sLen
|
|
pad = replicate pLen '0'
|
|
in pad ++ str
|
|
|
|
-- | Aligns text to the left within a field (clipping if necessary)
|
|
left
|
|
:: Int
|
|
-- ^ The width of the field
|
|
-> String
|
|
-- ^ The text to align
|
|
-> String
|
|
left n str = take n $ str ++ repeat ' '
|
|
|
|
-- | Aligns text to the right within a field (clipping if necessary)
|
|
right
|
|
:: Int
|
|
-- ^ The width of the field
|
|
-> String
|
|
-- ^ The text to align
|
|
-> String
|
|
right n str = reverse $ left n $ reverse str
|
|
|
|
-- | Aligns text to the centre within a field (clipping if necessary)
|
|
centre
|
|
:: Int
|
|
-- ^ The width of the field
|
|
-> String
|
|
-- ^ The text to align
|
|
-> String
|
|
centre n str = let
|
|
sLen = length str
|
|
pLen = (n - sLen) `div` 2
|
|
pad = replicate pLen ' '
|
|
in take n $ pad ++ str ++ repeat ' '
|
|
|
|
-- | Pads text on the right with spaces to fit a minimum width
|
|
padRight
|
|
:: Int
|
|
-- ^ The width to pad to
|
|
-> String
|
|
-- ^ The text to pad
|
|
-> String
|
|
padRight width str =
|
|
overlay str $ replicate width ' '
|
|
|
|
-- | Overlays one string on top of another
|
|
overlay
|
|
:: String
|
|
-- ^ The string on the top
|
|
-> String
|
|
-- ^ The string on the bottom
|
|
-> String
|
|
overlay (x:xs) (_:ys) = x : overlay xs ys
|
|
overlay xs [] = xs
|
|
overlay [] ys = ys
|
|
|
|
-- | Converts a number to a three character month (e.g. @"JAN"@)
|
|
month :: Int -> String
|
|
month 1 = "JAN"
|
|
month 2 = "FEB"
|
|
month 3 = "MAR"
|
|
month 4 = "APR"
|
|
month 5 = "MAY"
|
|
month 6 = "JUN"
|
|
month 7 = "JUL"
|
|
month 8 = "AUG"
|
|
month 9 = "SEP"
|
|
month 10 = "OCT"
|
|
month 11 = "NOV"
|
|
month 12 = "DEC"
|
|
month _ = ""
|
|
|
|
-- | Creates a two-column table with labels
|
|
labelTable :: [(String, String)] -> [String]
|
|
labelTable xs = let
|
|
labelWidth = maximum $ map (length . fst) xs
|
|
valWidth = maximum $ map (length . snd) xs
|
|
|
|
in map
|
|
( \(label, val)
|
|
-> right labelWidth label
|
|
++ ": "
|
|
++ left valWidth val
|
|
) xs
|
|
|
|
-- | Creates a variable column table of numbers with two axes
|
|
numTable
|
|
:: [String]
|
|
-- ^ The top column labels
|
|
-> [(String, [Int])]
|
|
-- ^ The rows with their labels
|
|
-> [String]
|
|
numTable headers rows = tableWith right $ header : body
|
|
where
|
|
header = "" : headers
|
|
body = map
|
|
(\(label, row) ->
|
|
label : map show row)
|
|
rows
|
|
|
|
-- | Creates a table from a two-dimensional list with a specified
|
|
-- padding function
|
|
tableWith
|
|
:: (Int -> String -> String)
|
|
-- ^ The padding function
|
|
-> [[String]]
|
|
-- ^ The cells
|
|
-> [String]
|
|
tableWith pFunc tData = complexTable
|
|
(repeat pFunc)
|
|
(map (map CellText) tData)
|
|
|
|
-- | Creates a complex table
|
|
complexTable
|
|
:: [Int -> String -> String]
|
|
-- ^ The padding function for each column
|
|
-> [[TableCell]]
|
|
-- ^ The table cells (an array of rows)
|
|
-> [String]
|
|
complexTable pFuncs tData = let
|
|
widths = map
|
|
(map $ \case
|
|
CellText str -> length str
|
|
CellFill _ -> 0)
|
|
tData
|
|
colWidths = map maximum $ transpose widths
|
|
|
|
bFunc = \case
|
|
[] -> ""
|
|
[(f, len, CellText str)] -> f len str
|
|
[(_, len, CellFill ch)] -> replicate len ch
|
|
(f, len, CellText str) : cells -> f len str ++ " " ++ bFunc cells
|
|
(_, len, CellFill ch) : cells -> replicate (succ len) ch ++ bFunc cells
|
|
|
|
in map
|
|
(bFunc . zip3 pFuncs colWidths)
|
|
tData
|
|
|
|
-- | Places an overlay on the last line of an report
|
|
overlayLast
|
|
:: String
|
|
-- ^ The text to overlay
|
|
-> [String]
|
|
-- ^ The report to modify
|
|
-> [String]
|
|
-- ^ The resulting report
|
|
overlayLast _ [] = []
|
|
overlayLast str [l] = [overlay str l]
|
|
overlayLast str (l:ls) = l : overlayLast str ls
|
|
|
|
-- | Converts a non-integer into a string
|
|
showFloating :: RealFrac n => n -> String
|
|
showFloating n = let
|
|
i = round $ n * 100
|
|
whole = i `div` 100
|
|
fraction = i `mod` 100
|
|
in show whole ++ "." ++ padNum 2 fraction
|