-- | Generic-ish (not really) pretty-printing utilities and abstractions for rendering the table of cells.
module ParkBench.Pretty
  ( -- * High-level row/cell machinery
    R (..),
    Cellular (..),
    BytesCell (..),
    BytesPerSecondCell (..),
    IncomparablePercentageCell (..),
    IncomparableWord3Cell (..),
    NanosecondsCell (..),
    NumberCell (..),
    NumberCell' (..),
    PercentageCell (..),
    PercentageCell' (..),
    rowMaker,

    -- *
    Table (..),
    renderTable,
    RowGroup (..),
    Row (..),
    Cell (..),
    isEmptyCell,
    Color (..),
  )
where

import Data.List.NonEmpty (NonEmpty (..))
import Data.Maybe
import Data.Ord (Down (..))
import Data.String (IsString (..))
import qualified Data.Text as Text
import ParkBench.Builder (Builder)
import qualified ParkBench.Builder as Builder
import ParkBench.Prelude
import qualified ParkBench.Terminal as Terminal

------------------------------------------------------------------------------------------------------------------------
-- High-level row/cell machinery

data R a b
  = R Cell (a -> Maybe b)

class Ord a => Cellular a where
  cellDelta :: a -> a -> Double
  cellString :: a -> Builder

newtype BytesCell
  = BytesCell Double
  deriving (BytesCell -> BytesCell -> Bool
(BytesCell -> BytesCell -> Bool)
-> (BytesCell -> BytesCell -> Bool) -> Eq BytesCell
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: BytesCell -> BytesCell -> Bool
$c/= :: BytesCell -> BytesCell -> Bool
== :: BytesCell -> BytesCell -> Bool
$c== :: BytesCell -> BytesCell -> Bool
Eq) via Double
  deriving (Eq BytesCell
Eq BytesCell
-> (BytesCell -> BytesCell -> Ordering)
-> (BytesCell -> BytesCell -> Bool)
-> (BytesCell -> BytesCell -> Bool)
-> (BytesCell -> BytesCell -> Bool)
-> (BytesCell -> BytesCell -> Bool)
-> (BytesCell -> BytesCell -> BytesCell)
-> (BytesCell -> BytesCell -> BytesCell)
-> Ord BytesCell
BytesCell -> BytesCell -> Bool
BytesCell -> BytesCell -> Ordering
BytesCell -> BytesCell -> BytesCell
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: BytesCell -> BytesCell -> BytesCell
$cmin :: BytesCell -> BytesCell -> BytesCell
max :: BytesCell -> BytesCell -> BytesCell
$cmax :: BytesCell -> BytesCell -> BytesCell
>= :: BytesCell -> BytesCell -> Bool
$c>= :: BytesCell -> BytesCell -> Bool
> :: BytesCell -> BytesCell -> Bool
$c> :: BytesCell -> BytesCell -> Bool
<= :: BytesCell -> BytesCell -> Bool
$c<= :: BytesCell -> BytesCell -> Bool
< :: BytesCell -> BytesCell -> Bool
$c< :: BytesCell -> BytesCell -> Bool
compare :: BytesCell -> BytesCell -> Ordering
$ccompare :: BytesCell -> BytesCell -> Ordering
$cp1Ord :: Eq BytesCell
Ord) via Down Double

instance Cellular BytesCell where
  cellDelta :: BytesCell -> BytesCell -> Double
cellDelta = (Double -> Double -> Double) -> BytesCell -> BytesCell -> Double
coerce Double -> Double -> Double
doubleDelta
  cellString :: BytesCell -> Builder
cellString (BytesCell Double
r) = Double -> Builder
Builder.bytes4 Double
r

newtype BytesPerSecondCell
  = BytesPerSecondCell Double
  deriving (BytesPerSecondCell -> BytesPerSecondCell -> Bool
(BytesPerSecondCell -> BytesPerSecondCell -> Bool)
-> (BytesPerSecondCell -> BytesPerSecondCell -> Bool)
-> Eq BytesPerSecondCell
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: BytesPerSecondCell -> BytesPerSecondCell -> Bool
$c/= :: BytesPerSecondCell -> BytesPerSecondCell -> Bool
== :: BytesPerSecondCell -> BytesPerSecondCell -> Bool
$c== :: BytesPerSecondCell -> BytesPerSecondCell -> Bool
Eq) via Double
  deriving (Eq BytesPerSecondCell
Eq BytesPerSecondCell
-> (BytesPerSecondCell -> BytesPerSecondCell -> Ordering)
-> (BytesPerSecondCell -> BytesPerSecondCell -> Bool)
-> (BytesPerSecondCell -> BytesPerSecondCell -> Bool)
-> (BytesPerSecondCell -> BytesPerSecondCell -> Bool)
-> (BytesPerSecondCell -> BytesPerSecondCell -> Bool)
-> (BytesPerSecondCell -> BytesPerSecondCell -> BytesPerSecondCell)
-> (BytesPerSecondCell -> BytesPerSecondCell -> BytesPerSecondCell)
-> Ord BytesPerSecondCell
BytesPerSecondCell -> BytesPerSecondCell -> Bool
BytesPerSecondCell -> BytesPerSecondCell -> Ordering
BytesPerSecondCell -> BytesPerSecondCell -> BytesPerSecondCell
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: BytesPerSecondCell -> BytesPerSecondCell -> BytesPerSecondCell
$cmin :: BytesPerSecondCell -> BytesPerSecondCell -> BytesPerSecondCell
max :: BytesPerSecondCell -> BytesPerSecondCell -> BytesPerSecondCell
$cmax :: BytesPerSecondCell -> BytesPerSecondCell -> BytesPerSecondCell
>= :: BytesPerSecondCell -> BytesPerSecondCell -> Bool
$c>= :: BytesPerSecondCell -> BytesPerSecondCell -> Bool
> :: BytesPerSecondCell -> BytesPerSecondCell -> Bool
$c> :: BytesPerSecondCell -> BytesPerSecondCell -> Bool
<= :: BytesPerSecondCell -> BytesPerSecondCell -> Bool
$c<= :: BytesPerSecondCell -> BytesPerSecondCell -> Bool
< :: BytesPerSecondCell -> BytesPerSecondCell -> Bool
$c< :: BytesPerSecondCell -> BytesPerSecondCell -> Bool
compare :: BytesPerSecondCell -> BytesPerSecondCell -> Ordering
$ccompare :: BytesPerSecondCell -> BytesPerSecondCell -> Ordering
$cp1Ord :: Eq BytesPerSecondCell
Ord) via Down Double

instance Cellular BytesPerSecondCell where
  cellDelta :: BytesPerSecondCell -> BytesPerSecondCell -> Double
cellDelta = (Double -> Double -> Double)
-> BytesPerSecondCell -> BytesPerSecondCell -> Double
coerce Double -> Double -> Double
doubleDelta
  cellString :: BytesPerSecondCell -> Builder
cellString (BytesPerSecondCell Double
r) = Double -> Builder
prettyBytesPerSecond Double
r

prettyBytesPerSecond :: Double -> Builder
prettyBytesPerSecond :: Double -> Builder
prettyBytesPerSecond Double
r =
  if Builder -> Bool
Builder.null Builder
s
    then Builder
Builder.empty
    else Builder
s Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"/s"
  where
    s :: Builder
s = Double -> Builder
Builder.bytes4 Double
r

newtype IncomparablePercentageCell
  = IncomparablePercentageCell Double
  deriving (IncomparablePercentageCell -> IncomparablePercentageCell -> Bool
(IncomparablePercentageCell -> IncomparablePercentageCell -> Bool)
-> (IncomparablePercentageCell
    -> IncomparablePercentageCell -> Bool)
-> Eq IncomparablePercentageCell
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: IncomparablePercentageCell -> IncomparablePercentageCell -> Bool
$c/= :: IncomparablePercentageCell -> IncomparablePercentageCell -> Bool
== :: IncomparablePercentageCell -> IncomparablePercentageCell -> Bool
$c== :: IncomparablePercentageCell -> IncomparablePercentageCell -> Bool
Eq) via Double
  deriving (Eq IncomparablePercentageCell
Eq IncomparablePercentageCell
-> (IncomparablePercentageCell
    -> IncomparablePercentageCell -> Ordering)
-> (IncomparablePercentageCell
    -> IncomparablePercentageCell -> Bool)
-> (IncomparablePercentageCell
    -> IncomparablePercentageCell -> Bool)
-> (IncomparablePercentageCell
    -> IncomparablePercentageCell -> Bool)
-> (IncomparablePercentageCell
    -> IncomparablePercentageCell -> Bool)
-> (IncomparablePercentageCell
    -> IncomparablePercentageCell -> IncomparablePercentageCell)
-> (IncomparablePercentageCell
    -> IncomparablePercentageCell -> IncomparablePercentageCell)
-> Ord IncomparablePercentageCell
IncomparablePercentageCell -> IncomparablePercentageCell -> Bool
IncomparablePercentageCell
-> IncomparablePercentageCell -> Ordering
IncomparablePercentageCell
-> IncomparablePercentageCell -> IncomparablePercentageCell
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: IncomparablePercentageCell
-> IncomparablePercentageCell -> IncomparablePercentageCell
$cmin :: IncomparablePercentageCell
-> IncomparablePercentageCell -> IncomparablePercentageCell
max :: IncomparablePercentageCell
-> IncomparablePercentageCell -> IncomparablePercentageCell
$cmax :: IncomparablePercentageCell
-> IncomparablePercentageCell -> IncomparablePercentageCell
>= :: IncomparablePercentageCell -> IncomparablePercentageCell -> Bool
$c>= :: IncomparablePercentageCell -> IncomparablePercentageCell -> Bool
> :: IncomparablePercentageCell -> IncomparablePercentageCell -> Bool
$c> :: IncomparablePercentageCell -> IncomparablePercentageCell -> Bool
<= :: IncomparablePercentageCell -> IncomparablePercentageCell -> Bool
$c<= :: IncomparablePercentageCell -> IncomparablePercentageCell -> Bool
< :: IncomparablePercentageCell -> IncomparablePercentageCell -> Bool
$c< :: IncomparablePercentageCell -> IncomparablePercentageCell -> Bool
compare :: IncomparablePercentageCell
-> IncomparablePercentageCell -> Ordering
$ccompare :: IncomparablePercentageCell
-> IncomparablePercentageCell -> Ordering
$cp1Ord :: Eq IncomparablePercentageCell
Ord) via Down Double

instance Cellular IncomparablePercentageCell where
  cellDelta :: IncomparablePercentageCell -> IncomparablePercentageCell -> Double
cellDelta IncomparablePercentageCell
_ IncomparablePercentageCell
_ = Double
0
  cellString :: IncomparablePercentageCell -> Builder
cellString (IncomparablePercentageCell Double
r) = Double -> Builder
Builder.percentage Double
r

newtype NumberCell
  = NumberCell Double
  deriving (NumberCell -> NumberCell -> Bool
(NumberCell -> NumberCell -> Bool)
-> (NumberCell -> NumberCell -> Bool) -> Eq NumberCell
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: NumberCell -> NumberCell -> Bool
$c/= :: NumberCell -> NumberCell -> Bool
== :: NumberCell -> NumberCell -> Bool
$c== :: NumberCell -> NumberCell -> Bool
Eq) via Double
  deriving (Eq NumberCell
Eq NumberCell
-> (NumberCell -> NumberCell -> Ordering)
-> (NumberCell -> NumberCell -> Bool)
-> (NumberCell -> NumberCell -> Bool)
-> (NumberCell -> NumberCell -> Bool)
-> (NumberCell -> NumberCell -> Bool)
-> (NumberCell -> NumberCell -> NumberCell)
-> (NumberCell -> NumberCell -> NumberCell)
-> Ord NumberCell
NumberCell -> NumberCell -> Bool
NumberCell -> NumberCell -> Ordering
NumberCell -> NumberCell -> NumberCell
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: NumberCell -> NumberCell -> NumberCell
$cmin :: NumberCell -> NumberCell -> NumberCell
max :: NumberCell -> NumberCell -> NumberCell
$cmax :: NumberCell -> NumberCell -> NumberCell
>= :: NumberCell -> NumberCell -> Bool
$c>= :: NumberCell -> NumberCell -> Bool
> :: NumberCell -> NumberCell -> Bool
$c> :: NumberCell -> NumberCell -> Bool
<= :: NumberCell -> NumberCell -> Bool
$c<= :: NumberCell -> NumberCell -> Bool
< :: NumberCell -> NumberCell -> Bool
$c< :: NumberCell -> NumberCell -> Bool
compare :: NumberCell -> NumberCell -> Ordering
$ccompare :: NumberCell -> NumberCell -> Ordering
$cp1Ord :: Eq NumberCell
Ord) via Down Double

instance Cellular NumberCell where
  cellDelta :: NumberCell -> NumberCell -> Double
cellDelta = (Double -> Double -> Double) -> NumberCell -> NumberCell -> Double
coerce Double -> Double -> Double
doubleDelta
  cellString :: NumberCell -> Builder
cellString (NumberCell Double
r) = Double -> Builder
Builder.double4 Double
r

newtype NumberCell'
  = NumberCell' Double
  deriving (NumberCell' -> NumberCell' -> Bool
(NumberCell' -> NumberCell' -> Bool)
-> (NumberCell' -> NumberCell' -> Bool) -> Eq NumberCell'
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: NumberCell' -> NumberCell' -> Bool
$c/= :: NumberCell' -> NumberCell' -> Bool
== :: NumberCell' -> NumberCell' -> Bool
$c== :: NumberCell' -> NumberCell' -> Bool
Eq, Eq NumberCell'
Eq NumberCell'
-> (NumberCell' -> NumberCell' -> Ordering)
-> (NumberCell' -> NumberCell' -> Bool)
-> (NumberCell' -> NumberCell' -> Bool)
-> (NumberCell' -> NumberCell' -> Bool)
-> (NumberCell' -> NumberCell' -> Bool)
-> (NumberCell' -> NumberCell' -> NumberCell')
-> (NumberCell' -> NumberCell' -> NumberCell')
-> Ord NumberCell'
NumberCell' -> NumberCell' -> Bool
NumberCell' -> NumberCell' -> Ordering
NumberCell' -> NumberCell' -> NumberCell'
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: NumberCell' -> NumberCell' -> NumberCell'
$cmin :: NumberCell' -> NumberCell' -> NumberCell'
max :: NumberCell' -> NumberCell' -> NumberCell'
$cmax :: NumberCell' -> NumberCell' -> NumberCell'
>= :: NumberCell' -> NumberCell' -> Bool
$c>= :: NumberCell' -> NumberCell' -> Bool
> :: NumberCell' -> NumberCell' -> Bool
$c> :: NumberCell' -> NumberCell' -> Bool
<= :: NumberCell' -> NumberCell' -> Bool
$c<= :: NumberCell' -> NumberCell' -> Bool
< :: NumberCell' -> NumberCell' -> Bool
$c< :: NumberCell' -> NumberCell' -> Bool
compare :: NumberCell' -> NumberCell' -> Ordering
$ccompare :: NumberCell' -> NumberCell' -> Ordering
$cp1Ord :: Eq NumberCell'
Ord) via Double
  deriving (Ord NumberCell'
Ord NumberCell'
-> (NumberCell' -> NumberCell' -> Double)
-> (NumberCell' -> Builder)
-> Cellular NumberCell'
NumberCell' -> Builder
NumberCell' -> NumberCell' -> Double
forall a.
Ord a -> (a -> a -> Double) -> (a -> Builder) -> Cellular a
cellString :: NumberCell' -> Builder
$ccellString :: NumberCell' -> Builder
cellDelta :: NumberCell' -> NumberCell' -> Double
$ccellDelta :: NumberCell' -> NumberCell' -> Double
$cp1Cellular :: Ord NumberCell'
Cellular) via NumberCell

newtype NanosecondsCell
  = NanosecondsCell Double
  deriving (NanosecondsCell -> NanosecondsCell -> Bool
(NanosecondsCell -> NanosecondsCell -> Bool)
-> (NanosecondsCell -> NanosecondsCell -> Bool)
-> Eq NanosecondsCell
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: NanosecondsCell -> NanosecondsCell -> Bool
$c/= :: NanosecondsCell -> NanosecondsCell -> Bool
== :: NanosecondsCell -> NanosecondsCell -> Bool
$c== :: NanosecondsCell -> NanosecondsCell -> Bool
Eq) via Double
  deriving (Eq NanosecondsCell
Eq NanosecondsCell
-> (NanosecondsCell -> NanosecondsCell -> Ordering)
-> (NanosecondsCell -> NanosecondsCell -> Bool)
-> (NanosecondsCell -> NanosecondsCell -> Bool)
-> (NanosecondsCell -> NanosecondsCell -> Bool)
-> (NanosecondsCell -> NanosecondsCell -> Bool)
-> (NanosecondsCell -> NanosecondsCell -> NanosecondsCell)
-> (NanosecondsCell -> NanosecondsCell -> NanosecondsCell)
-> Ord NanosecondsCell
NanosecondsCell -> NanosecondsCell -> Bool
NanosecondsCell -> NanosecondsCell -> Ordering
NanosecondsCell -> NanosecondsCell -> NanosecondsCell
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: NanosecondsCell -> NanosecondsCell -> NanosecondsCell
$cmin :: NanosecondsCell -> NanosecondsCell -> NanosecondsCell
max :: NanosecondsCell -> NanosecondsCell -> NanosecondsCell
$cmax :: NanosecondsCell -> NanosecondsCell -> NanosecondsCell
>= :: NanosecondsCell -> NanosecondsCell -> Bool
$c>= :: NanosecondsCell -> NanosecondsCell -> Bool
> :: NanosecondsCell -> NanosecondsCell -> Bool
$c> :: NanosecondsCell -> NanosecondsCell -> Bool
<= :: NanosecondsCell -> NanosecondsCell -> Bool
$c<= :: NanosecondsCell -> NanosecondsCell -> Bool
< :: NanosecondsCell -> NanosecondsCell -> Bool
$c< :: NanosecondsCell -> NanosecondsCell -> Bool
compare :: NanosecondsCell -> NanosecondsCell -> Ordering
$ccompare :: NanosecondsCell -> NanosecondsCell -> Ordering
$cp1Ord :: Eq NanosecondsCell
Ord) via Down Double

instance Cellular NanosecondsCell where
  cellDelta :: NanosecondsCell -> NanosecondsCell -> Double
cellDelta = (Double -> Double -> Double)
-> NanosecondsCell -> NanosecondsCell -> Double
coerce Double -> Double -> Double
doubleDelta
  cellString :: NanosecondsCell -> Builder
cellString (NanosecondsCell Double
r) = Double -> Builder
Builder.nanos4 Double
r

newtype PercentageCell
  = PercentageCell Double
  deriving (PercentageCell -> PercentageCell -> Bool
(PercentageCell -> PercentageCell -> Bool)
-> (PercentageCell -> PercentageCell -> Bool) -> Eq PercentageCell
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: PercentageCell -> PercentageCell -> Bool
$c/= :: PercentageCell -> PercentageCell -> Bool
== :: PercentageCell -> PercentageCell -> Bool
$c== :: PercentageCell -> PercentageCell -> Bool
Eq) via Double
  deriving (Eq PercentageCell
Eq PercentageCell
-> (PercentageCell -> PercentageCell -> Ordering)
-> (PercentageCell -> PercentageCell -> Bool)
-> (PercentageCell -> PercentageCell -> Bool)
-> (PercentageCell -> PercentageCell -> Bool)
-> (PercentageCell -> PercentageCell -> Bool)
-> (PercentageCell -> PercentageCell -> PercentageCell)
-> (PercentageCell -> PercentageCell -> PercentageCell)
-> Ord PercentageCell
PercentageCell -> PercentageCell -> Bool
PercentageCell -> PercentageCell -> Ordering
PercentageCell -> PercentageCell -> PercentageCell
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: PercentageCell -> PercentageCell -> PercentageCell
$cmin :: PercentageCell -> PercentageCell -> PercentageCell
max :: PercentageCell -> PercentageCell -> PercentageCell
$cmax :: PercentageCell -> PercentageCell -> PercentageCell
>= :: PercentageCell -> PercentageCell -> Bool
$c>= :: PercentageCell -> PercentageCell -> Bool
> :: PercentageCell -> PercentageCell -> Bool
$c> :: PercentageCell -> PercentageCell -> Bool
<= :: PercentageCell -> PercentageCell -> Bool
$c<= :: PercentageCell -> PercentageCell -> Bool
< :: PercentageCell -> PercentageCell -> Bool
$c< :: PercentageCell -> PercentageCell -> Bool
compare :: PercentageCell -> PercentageCell -> Ordering
$ccompare :: PercentageCell -> PercentageCell -> Ordering
$cp1Ord :: Eq PercentageCell
Ord) via Down Double

instance Cellular PercentageCell where
  cellDelta :: PercentageCell -> PercentageCell -> Double
cellDelta = (Double -> Double -> Double)
-> PercentageCell -> PercentageCell -> Double
coerce Double -> Double -> Double
doubleDelta
  cellString :: PercentageCell -> Builder
cellString (PercentageCell Double
r) = Double -> Builder
Builder.percentage Double
r

prettyDelta :: Double -> Builder
prettyDelta :: Double -> Builder
prettyDelta Double
n
  | Double
n Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
>= Double
1 = Double -> Builder
Builder.double4 (Double
n Double -> Double -> Double
forall a. Num a => a -> a -> a
+ Double
1) Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"x"
  | Double
n Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
<= -Double
0.5 = Double -> Builder
Builder.double4 (-Double
1 Double -> Double -> Double
`divideDouble` (Double
n Double -> Double -> Double
forall a. Num a => a -> a -> a
+ Double
1)) Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"x"
  | Bool
otherwise = Double -> Builder
Builder.percentage Double
n

newtype PercentageCell'
  = PercentageCell' Double
  deriving (PercentageCell' -> PercentageCell' -> Bool
(PercentageCell' -> PercentageCell' -> Bool)
-> (PercentageCell' -> PercentageCell' -> Bool)
-> Eq PercentageCell'
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: PercentageCell' -> PercentageCell' -> Bool
$c/= :: PercentageCell' -> PercentageCell' -> Bool
== :: PercentageCell' -> PercentageCell' -> Bool
$c== :: PercentageCell' -> PercentageCell' -> Bool
Eq, Eq PercentageCell'
Eq PercentageCell'
-> (PercentageCell' -> PercentageCell' -> Ordering)
-> (PercentageCell' -> PercentageCell' -> Bool)
-> (PercentageCell' -> PercentageCell' -> Bool)
-> (PercentageCell' -> PercentageCell' -> Bool)
-> (PercentageCell' -> PercentageCell' -> Bool)
-> (PercentageCell' -> PercentageCell' -> PercentageCell')
-> (PercentageCell' -> PercentageCell' -> PercentageCell')
-> Ord PercentageCell'
PercentageCell' -> PercentageCell' -> Bool
PercentageCell' -> PercentageCell' -> Ordering
PercentageCell' -> PercentageCell' -> PercentageCell'
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: PercentageCell' -> PercentageCell' -> PercentageCell'
$cmin :: PercentageCell' -> PercentageCell' -> PercentageCell'
max :: PercentageCell' -> PercentageCell' -> PercentageCell'
$cmax :: PercentageCell' -> PercentageCell' -> PercentageCell'
>= :: PercentageCell' -> PercentageCell' -> Bool
$c>= :: PercentageCell' -> PercentageCell' -> Bool
> :: PercentageCell' -> PercentageCell' -> Bool
$c> :: PercentageCell' -> PercentageCell' -> Bool
<= :: PercentageCell' -> PercentageCell' -> Bool
$c<= :: PercentageCell' -> PercentageCell' -> Bool
< :: PercentageCell' -> PercentageCell' -> Bool
$c< :: PercentageCell' -> PercentageCell' -> Bool
compare :: PercentageCell' -> PercentageCell' -> Ordering
$ccompare :: PercentageCell' -> PercentageCell' -> Ordering
$cp1Ord :: Eq PercentageCell'
Ord) via Double
  deriving (Ord PercentageCell'
Ord PercentageCell'
-> (PercentageCell' -> PercentageCell' -> Double)
-> (PercentageCell' -> Builder)
-> Cellular PercentageCell'
PercentageCell' -> Builder
PercentageCell' -> PercentageCell' -> Double
forall a.
Ord a -> (a -> a -> Double) -> (a -> Builder) -> Cellular a
cellString :: PercentageCell' -> Builder
$ccellString :: PercentageCell' -> Builder
cellDelta :: PercentageCell' -> PercentageCell' -> Double
$ccellDelta :: PercentageCell' -> PercentageCell' -> Double
$cp1Cellular :: Ord PercentageCell'
Cellular) via PercentageCell

newtype IncomparableWord3Cell
  = IncomparableWord3Cell Word64
  deriving (IncomparableWord3Cell -> IncomparableWord3Cell -> Bool
(IncomparableWord3Cell -> IncomparableWord3Cell -> Bool)
-> (IncomparableWord3Cell -> IncomparableWord3Cell -> Bool)
-> Eq IncomparableWord3Cell
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: IncomparableWord3Cell -> IncomparableWord3Cell -> Bool
$c/= :: IncomparableWord3Cell -> IncomparableWord3Cell -> Bool
== :: IncomparableWord3Cell -> IncomparableWord3Cell -> Bool
$c== :: IncomparableWord3Cell -> IncomparableWord3Cell -> Bool
Eq) via Word64
  deriving (Eq IncomparableWord3Cell
Eq IncomparableWord3Cell
-> (IncomparableWord3Cell -> IncomparableWord3Cell -> Ordering)
-> (IncomparableWord3Cell -> IncomparableWord3Cell -> Bool)
-> (IncomparableWord3Cell -> IncomparableWord3Cell -> Bool)
-> (IncomparableWord3Cell -> IncomparableWord3Cell -> Bool)
-> (IncomparableWord3Cell -> IncomparableWord3Cell -> Bool)
-> (IncomparableWord3Cell
    -> IncomparableWord3Cell -> IncomparableWord3Cell)
-> (IncomparableWord3Cell
    -> IncomparableWord3Cell -> IncomparableWord3Cell)
-> Ord IncomparableWord3Cell
IncomparableWord3Cell -> IncomparableWord3Cell -> Bool
IncomparableWord3Cell -> IncomparableWord3Cell -> Ordering
IncomparableWord3Cell
-> IncomparableWord3Cell -> IncomparableWord3Cell
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: IncomparableWord3Cell
-> IncomparableWord3Cell -> IncomparableWord3Cell
$cmin :: IncomparableWord3Cell
-> IncomparableWord3Cell -> IncomparableWord3Cell
max :: IncomparableWord3Cell
-> IncomparableWord3Cell -> IncomparableWord3Cell
$cmax :: IncomparableWord3Cell
-> IncomparableWord3Cell -> IncomparableWord3Cell
>= :: IncomparableWord3Cell -> IncomparableWord3Cell -> Bool
$c>= :: IncomparableWord3Cell -> IncomparableWord3Cell -> Bool
> :: IncomparableWord3Cell -> IncomparableWord3Cell -> Bool
$c> :: IncomparableWord3Cell -> IncomparableWord3Cell -> Bool
<= :: IncomparableWord3Cell -> IncomparableWord3Cell -> Bool
$c<= :: IncomparableWord3Cell -> IncomparableWord3Cell -> Bool
< :: IncomparableWord3Cell -> IncomparableWord3Cell -> Bool
$c< :: IncomparableWord3Cell -> IncomparableWord3Cell -> Bool
compare :: IncomparableWord3Cell -> IncomparableWord3Cell -> Ordering
$ccompare :: IncomparableWord3Cell -> IncomparableWord3Cell -> Ordering
$cp1Ord :: Eq IncomparableWord3Cell
Ord) via Down Word64

instance Cellular IncomparableWord3Cell where
  cellDelta :: IncomparableWord3Cell -> IncomparableWord3Cell -> Double
cellDelta IncomparableWord3Cell
_ IncomparableWord3Cell
_ = Double
0 -- incomparable
  cellString :: IncomparableWord3Cell -> Builder
cellString (IncomparableWord3Cell Word64
r) = Word64 -> Builder
Builder.word3 Word64
r

doubleDelta :: Double -> Double -> Double
doubleDelta :: Double -> Double -> Double
doubleDelta Double
v1 Double
v2 =
  (Double
v2 Double -> Double -> Double
forall a. Num a => a -> a -> a
- Double
v1) Double -> Double -> Double
`divideDouble` Double
v1

rowMaker :: forall a. NonEmpty a -> (forall b. Cellular b => R a b -> Row)
rowMaker :: NonEmpty a -> forall b. Cellular b => R a b -> Row
rowMaker (a
summary0 :| [a]
summaries0) (R Cell
name (a -> Maybe b
f :: a -> Maybe b)) =
  if (Cell -> Bool) -> [Cell] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Cell -> Bool
isEmptyCell [Cell]
cols
    then Row
EmptyRow
    else [Cell] -> Row
Row (Cell
name Cell -> [Cell] -> [Cell]
forall a. a -> [a] -> [a]
: [Cell]
cols)
  where
    cols :: [Cell]
    cols :: [Cell]
cols =
      Cell -> (b -> Cell) -> Maybe b -> Cell
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Cell
EmptyCell (Color -> Text -> Cell
Cell Color
White (Text -> Cell) -> (b -> Text) -> b -> Cell
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> Text
Builder.build (Builder -> Text) -> (b -> Builder) -> b -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. b -> Builder
forall a. Cellular a => a -> Builder
cellString) (a -> Maybe b
f a
summary0) Cell -> [Cell] -> [Cell]
forall a. a -> [a] -> [a]
: Maybe b -> [a] -> [Cell]
makeCols (a -> Maybe b
f a
summary0) [a]
summaries0
    -- TODO make this cleaner
    makeCols :: Maybe b -> [a] -> [Cell]
    makeCols :: Maybe b -> [a] -> [Cell]
makeCols Maybe b
s0 = \case
      [] ->
        case [a]
summaries0 of
          [] -> []
          [a]
_ ->
            case (a -> Maybe b
f a
summary0, a -> Maybe b
f ([a] -> a
forall a. [a] -> a
last [a]
summaries0)) of
              (Just b
v0, Just b
v1) -> [b -> b -> Cell
delta b
v0 b
v1]
              (Maybe b, Maybe b)
_ -> []
      a
s1 : [a]
ss ->
        case (Maybe b
s0, a -> Maybe b
f a
s1) of
          (Maybe b
Nothing, Just b
v1) -> Cell
EmptyCell Cell -> [Cell] -> [Cell]
forall a. a -> [a] -> [a]
: Color -> Text -> Cell
Cell Color
White (Builder -> Text
Builder.build (b -> Builder
forall a. Cellular a => a -> Builder
cellString b
v1)) Cell -> [Cell] -> [Cell]
forall a. a -> [a] -> [a]
: Maybe b -> [a] -> [Cell]
makeCols (b -> Maybe b
forall a. a -> Maybe a
Just b
v1) [a]
ss
          (Just b
v0, Just b
v1) -> b -> b -> Cell
delta b
v0 b
v1 Cell -> [Cell] -> [Cell]
forall a. a -> [a] -> [a]
: Color -> Text -> Cell
Cell Color
White (Builder -> Text
Builder.build (b -> Builder
forall a. Cellular a => a -> Builder
cellString b
v1)) Cell -> [Cell] -> [Cell]
forall a. a -> [a] -> [a]
: Maybe b -> [a] -> [Cell]
makeCols (b -> Maybe b
forall a. a -> Maybe a
Just b
v1) [a]
ss
          (Maybe b
_, Maybe b
Nothing) -> Cell
EmptyCell Cell -> [Cell] -> [Cell]
forall a. a -> [a] -> [a]
: Cell
EmptyCell Cell -> [Cell] -> [Cell]
forall a. a -> [a] -> [a]
: Maybe b -> [a] -> [Cell]
makeCols Maybe b
forall a. Maybe a
Nothing [a]
ss
    delta :: b -> b -> Cell
    delta :: b -> b -> Cell
delta b
v1 b
v2 =
      if Builder -> Bool
Builder.null (b -> Builder
forall a. Cellular a => a -> Builder
cellString b
v1) Bool -> Bool -> Bool
|| Builder -> Bool
Builder.null (b -> Builder
forall a. Cellular a => a -> Builder
cellString b
v2)
        then Cell
EmptyCell
        else Builder -> Cell
colorize (Double -> Builder
prettyDelta (b -> b -> Double
forall a. Cellular a => a -> a -> Double
cellDelta b
v1 b
v2))
      where
        colorize :: Builder -> Cell
        colorize :: Builder -> Cell
colorize =
          ( case b -> b -> Ordering
forall a. Ord a => a -> a -> Ordering
compare b
v1 b
v2 of
              Ordering
LT -> Color -> Text -> Cell
Cell Color
Green
              Ordering
EQ -> Color -> Text -> Cell
Cell Color
White
              Ordering
GT -> Color -> Text -> Cell
Cell Color
Red
          )
            (Text -> Cell) -> (Builder -> Text) -> Builder -> Cell
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> Text
Builder.build

data Table
  = Table ![Cell] ![RowGroup]

data RowGroup
  = RowGroup {-# UNPACK #-} !Text ![Row]

data Row
  = -- | Invariant: 1+ cells; not all cells are empty
    Row ![Cell]
  | EmptyRow

data Cell
  = EmptyCell
  | Cell !Color {-# UNPACK #-} !Text

cellBuilder :: Cell -> Builder
cellBuilder :: Cell -> Builder
cellBuilder = \case
  Cell
EmptyCell -> Builder
Builder.empty
  Cell Color
color (Text -> Builder
Builder.t -> Builder
s) ->
    case Color
color of
      Color
Blue -> Builder -> Builder
Terminal.blue Builder
s
      Color
Green -> Builder -> Builder
Terminal.green Builder
s
      Color
Red -> Builder -> Builder
Terminal.red Builder
s
      Color
White -> Builder
s

cellWidth :: Cell -> Int
cellWidth :: Cell -> Int
cellWidth = \case
  Cell Color
_ Text
s -> Text -> Int
Text.length Text
s
  Cell
EmptyCell -> Int
0

instance IsString Cell where
  fromString :: String -> Cell
fromString =
    Color -> Text -> Cell
Cell Color
White (Text -> Cell) -> (String -> Text) -> String -> Cell
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
Text.pack

data Color
  = Blue
  | Green
  | Red
  | White

isEmptyCell :: Cell -> Bool
isEmptyCell :: Cell -> Bool
isEmptyCell = \case
  -- don't yet have invariant that s is non-null
  Cell Color
_ Text
s -> Text -> Bool
Text.null Text
s
  Cell
EmptyCell -> Bool
True

data Align
  = AlignLeft
  | AlignRight

renderTable :: Table -> Builder
renderTable :: Table -> Builder
renderTable (Table [Cell]
labels [RowGroup]
rowGroups) =
  (Builder
header Builder -> [Builder] -> [Builder]
forall a. a -> [a] -> [a]
: (RowGroup -> Maybe Builder) -> [RowGroup] -> [Builder]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe RowGroup -> Maybe Builder
renderRowGroup [RowGroup]
rowGroups [Builder] -> [Builder] -> [Builder]
forall a. [a] -> [a] -> [a]
++ [Builder
footer]) [Builder] -> Builder -> Builder
`Builder.sepBy` Char -> Builder
Builder.c Char
'\n'
  where
    header :: Builder
    header :: Builder
header =
      Char -> Builder
Builder.c Char
'┌'
        Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ((((Int, Cell) -> Builder) -> [(Int, Cell)] -> [Builder]
forall a b. (a -> b) -> [a] -> [b]
map (Align -> Char -> (Int, Cell) -> Builder
renderCell Align
AlignLeft Char
'─') ([Int] -> [Cell] -> [(Int, Cell)]
forall a b. [a] -> [b] -> [(a, b)]
zip ((Int -> Int) -> [Int] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map (Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
2) [Int]
widths) [Cell]
labels)) [Builder] -> Builder -> Builder
`Builder.sepBy` Char -> Builder
Builder.c Char
'┬')
        Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Char -> Builder
Builder.c Char
'┐'

    footer :: Builder
    footer :: Builder
footer =
      Char -> Builder
Builder.c Char
'└'
        Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> (((Int -> Builder) -> [Int] -> [Builder]
forall a b. (a -> b) -> [a] -> [b]
map (\Int
n -> Int -> Char -> Builder
Builder.cs (Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
2) Char
'─') [Int]
widths) [Builder] -> Builder -> Builder
`Builder.sepBy` Char -> Builder
Builder.c Char
'┴')
        Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Char -> Builder
Builder.c Char
'┘'

    renderRowGroup :: RowGroup -> Maybe Builder
    renderRowGroup :: RowGroup -> Maybe Builder
renderRowGroup (RowGroup Text
label [Row]
rows) =
      case (Row -> Maybe Builder) -> [Row] -> [Builder]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe Row -> Maybe Builder
renderRow [Row]
rows of
        [] -> Maybe Builder
forall a. Maybe a
Nothing
        [Builder]
s -> Builder -> Maybe Builder
forall a. a -> Maybe a
Just ((Builder
line Builder -> [Builder] -> [Builder]
forall a. a -> [a] -> [a]
: [Builder]
s) [Builder] -> Builder -> Builder
`Builder.sepBy` Char -> Builder
Builder.c Char
'\n')
      where
        line :: Builder
line =
          Char -> Builder
Builder.c Char
'├'
            Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"\ESC[1m\ESC[97m"
            Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Text -> Builder
Builder.t ((Char -> Char) -> Text -> Text
Text.map Char -> Char
dash Text
label)
            Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"\ESC[39m\ESC[22m"
            Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Int -> Char -> Builder
Builder.cs ([Int] -> Int
forall a. [a] -> a
head [Int]
widths Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
2 Int -> Int -> Int
forall a. Num a => a -> a -> a
- Text -> Int
Text.length Text
label) Char
'─'
            Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> (Int -> Builder) -> [Int] -> Builder
forall (t :: * -> *) m a.
(Foldable t, Monoid m) =>
(a -> m) -> t a -> m
foldMap (\Int
n -> Char -> Builder
Builder.c Char
'┼' Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Int -> Char -> Builder
Builder.cs (Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
2) Char
'─') ([Int] -> [Int]
forall a. [a] -> [a]
tail [Int]
widths)
            Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Char -> Builder
Builder.c Char
'┤'

        dash :: Char -> Char
        dash :: Char -> Char
dash = \case
          Char
' ' -> Char
'─'
          Char
c -> Char
c

    renderRow :: Row -> Maybe Builder
    renderRow :: Row -> Maybe Builder
renderRow = \case
      Row [Cell]
row -> Builder -> Maybe Builder
forall a. a -> Maybe a
Just (Builder
"│ " Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ((((Int, Cell) -> Builder) -> [(Int, Cell)] -> [Builder]
forall a b. (a -> b) -> [a] -> [b]
map (Align -> Char -> (Int, Cell) -> Builder
renderCell Align
AlignRight Char
' ') ([Int] -> [Cell] -> [(Int, Cell)]
forall a b. [a] -> [b] -> [(a, b)]
zip [Int]
widths [Cell]
row)) [Builder] -> Builder -> Builder
`Builder.sepBy` Builder
" │ ") Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
" │")
      Row
EmptyRow -> Maybe Builder
forall a. Maybe a
Nothing

    renderCell :: Align -> Char -> (Int, Cell) -> Builder
    renderCell :: Align -> Char -> (Int, Cell) -> Builder
renderCell Align
align Char
bg (Int
n, Cell
cell) =
      case Align
align of
        Align
AlignLeft -> Cell -> Builder
cellBuilder Cell
cell Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
padding
        Align
AlignRight -> Builder
padding Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Cell -> Builder
cellBuilder Cell
cell
      where
        padding :: Builder
padding = Int -> Char -> Builder
Builder.cs (Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
- Cell -> Int
cellWidth Cell
cell) Char
bg

    widths :: [Int]
    widths :: [Int]
widths =
      ([Int] -> RowGroup -> [Int]) -> [Int] -> [RowGroup] -> [Int]
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' [Int] -> RowGroup -> [Int]
step0 ((Cell -> Int) -> [Cell] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map (Int -> Int -> Int
forall a. Num a => a -> a -> a
subtract Int
1 (Int -> Int) -> (Cell -> Int) -> Cell -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Cell -> Int
cellWidth) [Cell]
labels) [RowGroup]
rowGroups
      where
        step0 :: [Int] -> RowGroup -> [Int]
        step0 :: [Int] -> RowGroup -> [Int]
step0 [Int]
acc (RowGroup Text
label [Row]
rows) =
          ([Int] -> Row -> [Int]) -> [Int] -> [Row] -> [Int]
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' (Text -> [Int] -> Row -> [Int]
step1 Text
label) [Int]
acc [Row]
rows

        step1 :: Text -> [Int] -> Row -> [Int]
        step1 :: Text -> [Int] -> Row -> [Int]
step1 Text
label [Int]
acc = \case
          Row [] -> String -> [Int]
forall a. HasCallStack => String -> a
error String
"empty row"
          Row (Cell
col : [Cell]
cols) -> (Int -> Int -> Int) -> [Int] -> [Int] -> [Int]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith Int -> Int -> Int
forall a. Ord a => a -> a -> a
max (Int -> Int -> Int
forall a. Ord a => a -> a -> a
max (Text -> Int
Text.length Text
label) (Cell -> Int
cellWidth Cell
col) Int -> [Int] -> [Int]
forall a. a -> [a] -> [a]
: (Cell -> Int) -> [Cell] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map Cell -> Int
cellWidth [Cell]
cols) [Int]
acc
          Row
EmptyRow -> [Int]
acc