    {- |
       Module      : Text.Pandoc.CSV
       Copyright   : Copyright (C) 2017–2020 John MacFarlane <jgm@berkeley.edu>
       License     : GNU GPL, version 2 or above
       Maintainer  : John MacFarlane <jgm@berkeley.edu>
       Stability   : alpha
       Portability : portable

    Simple CSV parser.
    -}

    module Text.Pandoc.CSV (
      CSVOptions(..),
      defaultCSVOptions,
      parseCSV,
      ParseError
    ) where

    import Control.Monad (unless, void)
    import Data.Text (Text)
    import qualified Data.Text as T
    import Text.Parsec
    import Text.Parsec.Text (Parser)

    data CSVOptions = CSVOptions{
        CSVOptions -> Char
    csvDelim     :: Char
      , CSVOptions -> Char
    csvQuote     :: Char
      , CSVOptions -> Bool
    csvKeepSpace :: Bool -- treat whitespace following delim as significant
      , CSVOptions -> Maybe Char
    csvEscape    :: Maybe Char -- default is to double up quote
    } deriving (ReadPrec [CSVOptions]
    ReadPrec CSVOptions
    Int -> ReadS CSVOptions
    ReadS [CSVOptions]
    (Int -> ReadS CSVOptions)
    -> ReadS [CSVOptions]
    -> ReadPrec CSVOptions
    -> ReadPrec [CSVOptions]
    -> Read CSVOptions
    forall a.
    (Int -> ReadS a)
    -> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
    readListPrec :: ReadPrec [CSVOptions]
    $creadListPrec :: ReadPrec [CSVOptions]
    readPrec :: ReadPrec CSVOptions
    $creadPrec :: ReadPrec CSVOptions
    readList :: ReadS [CSVOptions]
    $creadList :: ReadS [CSVOptions]
    readsPrec :: Int -> ReadS CSVOptions
    $creadsPrec :: Int -> ReadS CSVOptions
    Read, Int -> CSVOptions -> ShowS
    [CSVOptions] -> ShowS
    CSVOptions -> String
    (Int -> CSVOptions -> ShowS)
    -> (CSVOptions -> String)
    -> ([CSVOptions] -> ShowS)
    -> Show CSVOptions
    forall a.
    (Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
    showList :: [CSVOptions] -> ShowS
    $cshowList :: [CSVOptions] -> ShowS
    show :: CSVOptions -> String
    $cshow :: CSVOptions -> String
    showsPrec :: Int -> CSVOptions -> ShowS
    $cshowsPrec :: Int -> CSVOptions -> ShowS
    Show)

    defaultCSVOptions :: CSVOptions
    defaultCSVOptions :: CSVOptions
    defaultCSVOptions = CSVOptions :: Char -> Char -> Bool -> Maybe Char -> CSVOptions
    CSVOptions{
        csvDelim :: Char
    csvDelim = ','
      , csvQuote :: Char
    csvQuote = '"'
      , csvKeepSpace :: Bool
    csvKeepSpace = Bool
    False
      , csvEscape :: Maybe Char
    csvEscape = Maybe Char
    forall a. Maybe a
    Nothing }

    parseCSV :: CSVOptions -> Text -> Either ParseError [[Text]]
    parseCSV :: CSVOptions -> Text -> Either ParseError [[Text]]
    parseCSV opts :: CSVOptions
    opts t :: Text
    t = Parsec Text () [[Text]]
    -> String -> Text -> Either ParseError [[Text]]
    forall s t a.
    Stream s Identity t =>
    Parsec s () a -> String -> s -> Either ParseError a
    parse (CSVOptions -> Parsec Text () [[Text]]
    pCSV CSVOptions
    opts) "csv" Text
    t

    pCSV :: CSVOptions -> Parser [[Text]]
    pCSV :: CSVOptions -> Parsec Text () [[Text]]
    pCSV opts :: CSVOptions
    opts =
      (CSVOptions -> Parser [Text]
    pCSVRow CSVOptions
    opts Parser [Text]
    -> ParsecT Text () Identity () -> Parsec Text () [[Text]]
    forall s (m :: * -> *) t u a sep.
    Stream s m t =>
    ParsecT s u m a -> ParsecT s u m sep -> ParsecT s u m [a]
    `sepEndBy` ParsecT Text () Identity ()
    endline) Parsec Text () [[Text]]
    -> ParsecT Text () Identity () -> Parsec Text () [[Text]]
    forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
    <* (ParsecT Text () Identity ()
    forall s (m :: * -> *) u. Stream s m Char => ParsecT s u m ()
    spaces ParsecT Text () Identity ()
    -> ParsecT Text () Identity () -> ParsecT Text () Identity ()
    forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
    *> ParsecT Text () Identity ()
    forall s (m :: * -> *) t u.
    (Stream s m t, Show t) =>
    ParsecT s u m ()
    eof)

    pCSVRow :: CSVOptions -> Parser [Text]
    pCSVRow :: CSVOptions -> Parser [Text]
    pCSVRow opts :: CSVOptions
    opts = do
      Text
    x <- CSVOptions -> Parser Text
    pCSVCell CSVOptions
    opts
      [Text]
    xs <- (if Text -> Bool
    T.null Text
    x then Parser Text -> Parser [Text]
    forall s (m :: * -> *) t u a.
    Stream s m t =>
    ParsecT s u m a -> ParsecT s u m [a]
    many1 else Parser Text -> Parser [Text]
    forall s u (m :: * -> *) a. ParsecT s u m a -> ParsecT s u m [a]
    many) (Parser Text -> Parser [Text]) -> Parser Text -> Parser [Text]
    forall a b. (a -> b) -> a -> b
    $ CSVOptions -> ParsecT Text () Identity ()
    pCSVDelim CSVOptions
    opts ParsecT Text () Identity () -> Parser Text -> Parser Text
    forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
    *> CSVOptions -> Parser Text
    pCSVCell CSVOptions
    opts
      [Text] -> Parser [Text]
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Text
    xText -> [Text] -> [Text]
    forall a. a -> [a] -> [a]
    :[Text]
    xs)

    pCSVCell :: CSVOptions -> Parser Text
    pCSVCell :: CSVOptions -> Parser Text
    pCSVCell opts :: CSVOptions
    opts = CSVOptions -> Parser Text
    pCSVQuotedCell CSVOptions
    opts Parser Text -> Parser Text -> Parser Text
    forall s u (m :: * -> *) a.
    ParsecT s u m a -> ParsecT s u m a -> ParsecT s u m a
    <|> CSVOptions -> Parser Text
    pCSVUnquotedCell CSVOptions
    opts

    pCSVQuotedCell :: CSVOptions -> Parser Text
    pCSVQuotedCell :: CSVOptions -> Parser Text
    pCSVQuotedCell opts :: CSVOptions
    opts = do
      Char -> ParsecT Text () Identity Char
    forall s (m :: * -> *) u.
    Stream s m Char =>
    Char -> ParsecT s u m Char
    char (CSVOptions -> Char
    csvQuote CSVOptions
    opts)
      String
    res <- ParsecT Text () Identity Char -> ParsecT Text () Identity String
    forall s u (m :: * -> *) a. ParsecT s u m a -> ParsecT s u m [a]
    many ((Char -> Bool) -> ParsecT Text () Identity Char
    forall s (m :: * -> *) u.
    Stream s m Char =>
    (Char -> Bool) -> ParsecT s u m Char
    satisfy (\c :: Char
    c -> Char
    c Char -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    /= CSVOptions -> Char
    csvQuote CSVOptions
    opts Bool -> Bool -> Bool
    &&
                                  Char -> Maybe Char
    forall a. a -> Maybe a
    Just Char
    c Maybe Char -> Maybe Char -> Bool
    forall a. Eq a => a -> a -> Bool
    /= CSVOptions -> Maybe Char
    csvEscape CSVOptions
    opts) ParsecT Text () Identity Char
    -> ParsecT Text () Identity Char -> ParsecT Text () Identity Char
    forall s u (m :: * -> *) a.
    ParsecT s u m a -> ParsecT s u m a -> ParsecT s u m a
    <|> CSVOptions -> ParsecT Text () Identity Char
    escaped CSVOptions
    opts)
      Char -> ParsecT Text () Identity Char
    forall s (m :: * -> *) u.
    Stream s m Char =>
    Char -> ParsecT s u m Char
    char (CSVOptions -> Char
    csvQuote CSVOptions
    opts)
      Text -> Parser Text
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Text -> Parser Text) -> Text -> Parser Text
    forall a b. (a -> b) -> a -> b
    $ String -> Text
    T.pack String
    res

    escaped :: CSVOptions -> Parser Char
    escaped :: CSVOptions -> ParsecT Text () Identity Char
    escaped opts :: CSVOptions
    opts = ParsecT Text () Identity Char -> ParsecT Text () Identity Char
    forall s u (m :: * -> *) a. ParsecT s u m a -> ParsecT s u m a
    try (ParsecT Text () Identity Char -> ParsecT Text () Identity Char)
    -> ParsecT Text () Identity Char -> ParsecT Text () Identity Char
    forall a b. (a -> b) -> a -> b
    $
      case CSVOptions -> Maybe Char
    csvEscape CSVOptions
    opts of
           Nothing -> Char -> ParsecT Text () Identity Char
    forall s (m :: * -> *) u.
    Stream s m Char =>
    Char -> ParsecT s u m Char
    char (CSVOptions -> Char
    csvQuote CSVOptions
    opts) ParsecT Text () Identity Char
    -> ParsecT Text () Identity Char -> ParsecT Text () Identity Char
    forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
    >> Char -> ParsecT Text () Identity Char
    forall s (m :: * -> *) u.
    Stream s m Char =>
    Char -> ParsecT s u m Char
    char (CSVOptions -> Char
    csvQuote CSVOptions
    opts)
           Just c :: Char
    c  -> Char -> ParsecT Text () Identity Char
    forall s (m :: * -> *) u.
    Stream s m Char =>
    Char -> ParsecT s u m Char
    char Char
    c ParsecT Text () Identity Char
    -> ParsecT Text () Identity Char -> ParsecT Text () Identity Char
    forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
    >> String -> ParsecT Text () Identity Char
    forall s (m :: * -> *) u.
    Stream s m Char =>
    String -> ParsecT s u m Char
    noneOf "\r\n"

    pCSVUnquotedCell :: CSVOptions -> Parser Text
    pCSVUnquotedCell :: CSVOptions -> Parser Text
    pCSVUnquotedCell opts :: CSVOptions
    opts = String -> Text
    T.pack (String -> Text) -> ParsecT Text () Identity String -> Parser Text
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$>
      ParsecT Text () Identity Char -> ParsecT Text () Identity String
    forall s u (m :: * -> *) a. ParsecT s u m a -> ParsecT s u m [a]
    many ((Char -> Bool) -> ParsecT Text () Identity Char
    forall s (m :: * -> *) u.
    Stream s m Char =>
    (Char -> Bool) -> ParsecT s u m Char
    satisfy (\c :: Char
    c -> Char
    c Char -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    /= CSVOptions -> Char
    csvDelim CSVOptions
    opts Bool -> Bool -> Bool
    && Char
    c Char -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    /= '\r' Bool -> Bool -> Bool
    && Char
    c Char -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    /= '\n'
                      Bool -> Bool -> Bool
    && Char
    c Char -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    /= CSVOptions -> Char
    csvQuote CSVOptions
    opts))

    pCSVDelim :: CSVOptions -> Parser ()
    pCSVDelim :: CSVOptions -> ParsecT Text () Identity ()
    pCSVDelim opts :: CSVOptions
    opts = do
      Char -> ParsecT Text () Identity Char
    forall s (m :: * -> *) u.
    Stream s m Char =>
    Char -> ParsecT s u m Char
    char (CSVOptions -> Char
    csvDelim CSVOptions
    opts)
      Bool -> ParsecT Text () Identity () -> ParsecT Text () Identity ()
    forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
    unless (CSVOptions -> Bool
    csvKeepSpace CSVOptions
    opts) (ParsecT Text () Identity () -> ParsecT Text () Identity ())
    -> ParsecT Text () Identity () -> ParsecT Text () Identity ()
    forall a b. (a -> b) -> a -> b
    $ ParsecT Text () Identity Char -> ParsecT Text () Identity ()
    forall s u (m :: * -> *) a. ParsecT s u m a -> ParsecT s u m ()
    skipMany (String -> ParsecT Text () Identity Char
    forall s (m :: * -> *) u.
    Stream s m Char =>
    String -> ParsecT s u m Char
    oneOf " \t")

    endline :: Parser ()
    endline :: ParsecT Text () Identity ()
    endline = do
      ParsecT Text () Identity () -> ParsecT Text () Identity ()
    forall s (m :: * -> *) t u a.
    Stream s m t =>
    ParsecT s u m a -> ParsecT s u m ()
    optional (ParsecT Text () Identity Char -> ParsecT Text () Identity ()
    forall (f :: * -> *) a. Functor f => f a -> f ()
    void (ParsecT Text () Identity Char -> ParsecT Text () Identity ())
    -> ParsecT Text () Identity Char -> ParsecT Text () Identity ()
    forall a b. (a -> b) -> a -> b
    $ Char -> ParsecT Text () Identity Char
    forall s (m :: * -> *) u.
    Stream s m Char =>
    Char -> ParsecT s u m Char
    char '\r')
      ParsecT Text () Identity Char -> ParsecT Text () Identity ()
    forall (f :: * -> *) a. Functor f => f a -> f ()
    void (ParsecT Text () Identity Char -> ParsecT Text () Identity ())
    -> ParsecT Text () Identity Char -> ParsecT Text () Identity ()
    forall a b. (a -> b) -> a -> b
    $ Char -> ParsecT Text () Identity Char
    forall s (m :: * -> *) u.
    Stream s m Char =>
    Char -> ParsecT s u m Char
    char '\n'