packages feed

tablize-1.0.0: src/Main.hs

{-# LANGUAGE OverloadedStrings #-}

import Control.Applicative
import Options.Applicative
import System.Exit
import Text.Comma
import Text.Tabl
import qualified Data.Attoparsec.Text as A
import qualified Data.Text as T
import qualified Data.Text.IO as T

import Options

-- | Parse the visual decoration settings.
parseDecor :: A.Parser Decoration -- ^ parser
parseDecor = A.string "all"     *> pure DecorAll
         <|> A.string "none"    *> pure DecorNone
         <|> A.string "inner"   *> pure DecorInner
         <|> A.string "outer"   *> pure DecorOuter
         <|> A.string "only("   *> (DecorOnly   <$> decimal) <* A.char ')'
         <|> A.string "except(" *> (DecorExcept <$> decimal) <* A.char ')'
         <|> A.string "union("  *> (DecorUnion  <$> recurse) <* A.char ')'
         <|> A.string "isect("  *> (DecorIsect  <$> recurse) <* A.char ')'
         A.<?> "invalid decoration"
  where
    recurse = A.sepBy parseDecor (A.char ',')
    decimal = A.sepBy A.decimal  (A.char ',')

-- | Parse the column alignment settings.
parseAligns :: A.Parser [Alignment] -- ^ parser
parseAligns = (A.endOfInput *> pure []) <|> A.sepBy1 align (A.char ',')
  where
    align = A.string "left"   *> pure AlignLeft
        <|> A.string "centre" *> pure AlignCentre
        <|> A.string "right"  *> pure AlignRight
        A.<?> "invalid alignment"

-- | Create a table based on the file content and command-line options.
tablize :: Options              -- ^ command-line options
        -> [[T.Text]]           -- ^ CSV table
        -> Either String T.Text -- ^ error | table
tablize options cells = do
  hdecor <- A.parseOnly parseDecor  (optHorizontal options)
  vdecor <- A.parseOnly parseDecor  (optVertical options)
  aligns <- A.parseOnly parseAligns (optAlignment options)
  return $ tabl EnvAscii hdecor vdecor aligns cells

-- | Read the input file text. In case that no file was specified, the
-- standard input is used.
getInput :: Options   -- ^ command-line options
         -> IO T.Text -- ^ input
getInput options = case optFile options of
  Just file -> T.readFile file
  Nothing   -> T.getContents

-- | Pretty-printing of CSV files.
main :: IO ()
main = do
  options <- execParser Options.parser
  input   <- getInput options
  case comma input >>= tablize options of
    Left err  -> T.putStrLn ("ERROR: " <> T.pack err) >> exitFailure
    Right res -> T.putStrLn res                       >> exitSuccess