{-# LANGUAGE FlexibleContexts    #-}

{-# LANGUAGE ScopedTypeVariables #-}
{- |
   Module      : Text.Pandoc.Readers.CSV
   Copyright   : Copyright (C) 2006-2023 John MacFarlane
   License     : GNU GPL, version 2 or above

   Maintainer  : John MacFarlane <jgm@berkeley.edu>
   Stability   : alpha
   Portability : portable

Conversion from CSV or TSV to a 'Pandoc' table.
-}
module Text.Pandoc.Readers.CSV (
  readCSV,
  readTSV
) where
import qualified Data.Text as T
import Text.Pandoc.CSV (parseCSV, defaultCSVOptions, CSVOptions(..))
import Text.Pandoc.Definition
import qualified Text.Pandoc.Builder as B
import Text.Pandoc.Class (PandocMonad)
import Text.Pandoc.Sources (ToSources(..), sourcesToText)
import Text.Pandoc.Options (ReaderOptions)
import Control.Monad.Except (throwError)
import Data.Text (Text)
import Text.Pandoc.Parsing (fromParsecError)

readCSV :: (PandocMonad m, ToSources a)
        => ReaderOptions -- ^ Reader options
        -> a
        -> m Pandoc
readCSV :: forall (m :: * -> *) a.
(PandocMonad m, ToSources a) =>
ReaderOptions -> a -> m Pandoc
readCSV ReaderOptions
_opts a
s = do
  CSVOptions -> Text -> m Pandoc
forall (m :: * -> *).
PandocMonad m =>
CSVOptions -> Text -> m Pandoc
readCSVWith CSVOptions
defaultCSVOptions (Text -> m Pandoc) -> Text -> m Pandoc
forall a b. (a -> b) -> a -> b
$ Sources -> Text
sourcesToText (Sources -> Text) -> Sources -> Text
forall a b. (a -> b) -> a -> b
$ a -> Sources
forall a. ToSources a => a -> Sources
toSources a
s

readTSV :: (PandocMonad m, ToSources a)
        => ReaderOptions -- ^ Reader options
        -> a
        -> m Pandoc
readTSV :: forall (m :: * -> *) a.
(PandocMonad m, ToSources a) =>
ReaderOptions -> a -> m Pandoc
readTSV ReaderOptions
_opts a
s = do
  CSVOptions -> Text -> m Pandoc
forall (m :: * -> *).
PandocMonad m =>
CSVOptions -> Text -> m Pandoc
readCSVWith CSVOptions
tsvOpts (Text -> m Pandoc) -> Text -> m Pandoc
forall a b. (a -> b) -> a -> b
$ Sources -> Text
sourcesToText (Sources -> Text) -> Sources -> Text
forall a b. (a -> b) -> a -> b
$ a -> Sources
forall a. ToSources a => a -> Sources
toSources a
s
 where
  tsvOpts :: CSVOptions
tsvOpts = CSVOptions{
    csvDelim :: Char
csvDelim = Char
'\t',
    csvQuote :: Maybe Char
csvQuote = Maybe Char
forall a. Maybe a
Nothing,
    csvKeepSpace :: Bool
csvKeepSpace = Bool
False,
    csvEscape :: Maybe Char
csvEscape = Maybe Char
forall a. Maybe a
Nothing }

readCSVWith :: PandocMonad m
            => CSVOptions
            -> Text
            -> m Pandoc
readCSVWith :: forall (m :: * -> *).
PandocMonad m =>
CSVOptions -> Text -> m Pandoc
readCSVWith CSVOptions
csvopts Text
txt = do
  case CSVOptions -> Text -> Either ParseError [[Text]]
parseCSV CSVOptions
csvopts Text
txt of
    Right ([Text]
r:[[Text]]
rs) -> Pandoc -> m Pandoc
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Pandoc -> m Pandoc) -> Pandoc -> m Pandoc
forall a b. (a -> b) -> a -> b
$ Blocks -> Pandoc
B.doc (Blocks -> Pandoc) -> Blocks -> Pandoc
forall a b. (a -> b) -> a -> b
$ Caption
-> [ColSpec] -> TableHead -> [TableBody] -> TableFoot -> Blocks
B.table Caption
capt
                                             ([Alignment] -> [ColWidth] -> [ColSpec]
forall a b. [a] -> [b] -> [(a, b)]
zip [Alignment]
aligns [ColWidth]
widths)
                                             (Attr -> [Row] -> TableHead
TableHead Attr
nullAttr [Row]
hdrs)
                                             [Attr -> RowHeadColumns -> [Row] -> [Row] -> TableBody
TableBody Attr
nullAttr RowHeadColumns
0 [] [Row]
rows]
                                             (Attr -> [Row] -> TableFoot
TableFoot Attr
nullAttr [])
       where capt :: Caption
capt = Caption
B.emptyCaption
             numcols :: Int
numcols = [Text] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Text]
r
             toplain :: Text -> Cell
toplain = Blocks -> Cell
B.simpleCell (Blocks -> Cell) -> (Text -> Blocks) -> Text -> Cell
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Inlines -> Blocks
B.plain (Inlines -> Blocks) -> (Text -> Inlines) -> Text -> Blocks
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Inlines
B.text (Text -> Inlines) -> (Text -> Text) -> Text -> Inlines
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text
T.strip
             toRow :: [Text] -> Row
toRow = Attr -> [Cell] -> Row
Row Attr
nullAttr ([Cell] -> Row) -> ([Text] -> [Cell]) -> [Text] -> Row
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Text -> Cell) -> [Text] -> [Cell]
forall a b. (a -> b) -> [a] -> [b]
map Text -> Cell
toplain
             toHeaderRow :: [Text] -> [Row]
toHeaderRow [Text]
l = [[Text] -> Row
toRow [Text]
l | Bool -> Bool
not ([Text] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Text]
l)]
             hdrs :: [Row]
hdrs = [Text] -> [Row]
toHeaderRow [Text]
r
             rows :: [Row]
rows = ([Text] -> Row) -> [[Text]] -> [Row]
forall a b. (a -> b) -> [a] -> [b]
map [Text] -> Row
toRow [[Text]]
rs
             aligns :: [Alignment]
aligns = Int -> Alignment -> [Alignment]
forall a. Int -> a -> [a]
replicate Int
numcols Alignment
AlignDefault
             widths :: [ColWidth]
widths = Int -> ColWidth -> [ColWidth]
forall a. Int -> a -> [a]
replicate Int
numcols ColWidth
ColWidthDefault
    Right []     -> Pandoc -> m Pandoc
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Pandoc -> m Pandoc) -> Pandoc -> m Pandoc
forall a b. (a -> b) -> a -> b
$ Blocks -> Pandoc
B.doc Blocks
forall a. Monoid a => a
mempty
    Left ParseError
e       -> PandocError -> m Pandoc
forall a. PandocError -> m a
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError (PandocError -> m Pandoc) -> PandocError -> m Pandoc
forall a b. (a -> b) -> a -> b
$ Sources -> ParseError -> PandocError
fromParsecError ([(String, Text)] -> Sources
forall a. ToSources a => a -> Sources
toSources [(String
"",Text
txt)]) ParseError
e