module Colonnade.Encoding
(
headed
, headless
, singleton
, fromMaybe
, columns
, bool
, replaceWhen
, mapContent
, runRow
, runRowMonadic
, runRowMonadic_
, runRowMonadicWith
, runHeader
, runHeaderMonadic
, runHeaderMonadic_
, runHeaderMonadicGeneral
, runHeaderMonadicGeneral_
, runBothMonadic_
, ascii
) where
import Colonnade.Types
import Data.Vector (Vector)
import Data.Foldable
import Data.Monoid (Endo(..))
import Control.Monad
import Data.Functor.Contravariant
import qualified Data.Bool
import qualified Data.Maybe
import qualified Data.List as List
import qualified Data.Vector as Vector
import qualified Colonnade.Internal as Internal
headed :: c -> (a -> c) -> Colonnade Headed c a
headed h = singleton (Headed h)
headless :: (a -> c) -> Colonnade Headless c a
headless = singleton Headless
singleton :: f c -> (a -> c) -> Colonnade f c a
singleton h = Colonnade . Vector.singleton . OneColonnade h
fromMaybe :: c -> Colonnade f c a -> Colonnade f c (Maybe a)
fromMaybe c (Colonnade v) = Colonnade $ flip Vector.map v $
\(OneColonnade h encode) -> OneColonnade h (maybe c encode)
columns :: Foldable g
=> (b -> a -> c)
-> (b -> f c)
-> g b
-> Colonnade f c a
columns getCell getHeader = id
. Colonnade
. Vector.map (\b -> OneColonnade (getHeader b) (getCell b))
. Vector.fromList
. toList
bool ::
f c
-> (a -> Bool)
-> (a -> c)
-> (a -> c)
-> Colonnade f c a
bool h p onTrue onFalse = singleton h (Data.Bool.bool <$> onFalse <*> onTrue <*> p)
replaceWhen ::
c
-> (a -> Bool)
-> Colonnade f c a
-> Colonnade f c a
replaceWhen newContent p (Colonnade v) = Colonnade
( Vector.map
(\(OneColonnade h encode) -> OneColonnade h $ \a ->
if p a then newContent else encode a
) v
)
mapContent :: Functor f => (c1 -> c2) -> Colonnade f c1 a -> Colonnade f c2 a
mapContent f (Colonnade v) = Colonnade
$ Vector.map (\(OneColonnade h c) -> (OneColonnade (fmap f h) (f . c))) v
runRow :: (c1 -> c2) -> Colonnade f c1 a -> a -> Vector c2
runRow g (Colonnade v) a = flip Vector.map v $
\(OneColonnade _ encode) -> g (encode a)
runBothMonadic_ :: Monad m
=> Colonnade Headed content a
-> (content -> content -> m b)
-> a
-> m ()
runBothMonadic_ (Colonnade v) g a =
forM_ v $ \(OneColonnade (Headed h) encode) -> g h (encode a)
runRowMonadic :: (Monad m, Monoid b)
=> Colonnade f content a
-> (content -> m b)
-> a
-> m b
runRowMonadic (Colonnade v) g a =
flip Internal.foldlMapM v
$ \e -> g (oneColonnadeEncode e a)
runRowMonadic_ :: Monad m
=> Colonnade f content a
-> (content -> m b)
-> a
-> m ()
runRowMonadic_ (Colonnade v) g a =
forM_ v $ \e -> g (oneColonnadeEncode e a)
runRowMonadicWith :: (Monad m)
=> b
-> (b -> b -> b)
-> Colonnade f content a
-> (content -> m b)
-> a
-> m b
runRowMonadicWith bempty bappend (Colonnade v) g a =
foldlM (\bl e -> do
br <- g (oneColonnadeEncode e a)
return (bappend bl br)
) bempty v
runHeader :: (c1 -> c2) -> Colonnade Headed c1 a -> Vector c2
runHeader g (Colonnade v) =
Vector.map (g . getHeaded . oneColonnadeHead) v
runHeaderMonadicGeneral :: (Monad m, Monoid b, Foldable h)
=> Colonnade h content a
-> (content -> m b)
-> m b
runHeaderMonadicGeneral (Colonnade v) g = id
$ fmap (mconcat . Vector.toList)
$ Vector.mapM (Internal.foldlMapM g . oneColonnadeHead) v
runHeaderMonadic :: (Monad m, Monoid b)
=> Colonnade Headed content a
-> (content -> m b)
-> m b
runHeaderMonadic (Colonnade v) g =
fmap (mconcat . Vector.toList) $ Vector.mapM (g . getHeaded . oneColonnadeHead) v
runHeaderMonadicGeneral_ :: (Monad m, Monoid b, Foldable h)
=> Colonnade h content a
-> (content -> m b)
-> m ()
runHeaderMonadicGeneral_ (Colonnade v) g =
Vector.mapM_ (Internal.foldlMapM g . oneColonnadeHead) v
runHeaderMonadic_ ::
(Monad m)
=> Colonnade Headed content a
-> (content -> m b)
-> m ()
runHeaderMonadic_ (Colonnade v) g = Vector.mapM_ (g . getHeaded . oneColonnadeHead) v
ascii :: Foldable f
=> Colonnade Headed String a
-> f a
-> String
ascii enc xs =
let theHeader :: [(Int,String)]
theHeader = (zip (enumFrom 0) . map (\s -> " " ++ s ++ " ")) (toList (runHeader id enc))
theBody :: [[(Int,String)]]
theBody = map (zip (enumFrom 0) . map (\s -> " " ++ s ++ " ") . toList . runRow id enc) (toList xs)
sizes :: [Int]
sizes = ($ replicate (length theHeader) 1) $ appEndo $ mconcat
[ foldMap (\(i,str) -> Endo (replaceAt i (length str))) theHeader
, (foldMap . foldMap) (\(i,str) -> Endo (replaceAt i (length str))) theBody
]
paddedHeader :: [String]
paddedHeader = map (\(i,str) -> rightPad (atDef 1 sizes i) ' ' str) theHeader
paddedBody :: [[String]]
paddedBody = (map . map) (\(i,str) -> rightPad (atDef 1 sizes i) ' ' str) theBody
divider :: String
divider = "+" ++ join (List.intersperse "+" (map (\i -> replicate i '-') sizes)) ++ "+"
headerStr :: String
headerStr = "|" ++ join (List.intersperse "|" paddedHeader) ++ "|"
bodyStr :: String
bodyStr = List.unlines (map ((\s -> "|" ++ s ++ "|") . join . List.intersperse "|") paddedBody)
in divider ++ "\n" ++ headerStr
++ "\n" ++ divider
++ "\n" ++ bodyStr ++ divider ++ "\n"
replaceAt :: Ord a => Int -> a -> [a] -> [a]
replaceAt _ _ [] = []
replaceAt n v (a:as) = if n > 0
then a : replaceAt (n 1) v as
else (max v a) : as
rightPad :: Int -> a -> [a] -> [a]
rightPad m a xs = take m $ xs ++ repeat a
atDef :: a -> [a] -> Int -> a
atDef def = Data.Maybe.fromMaybe def .^ atMay where
(.^) f g x1 x2 = f (g x1 x2)
atMay = eitherToMaybe .^ at_
eitherToMaybe = either (const Nothing) Just
at_ xs o | o < 0 = Left $ "index must not be negative, index=" ++ show o
| otherwise = f o xs
where f 0 (z:_) = Right z
f i (_:zs) = f (i1) zs
f i [] = Left $ "index too large, index=" ++ show o ++ ", length=" ++ show (oi)