    {-# LANGUAGE Safe #-}
    {- arch-tag: CSV and TSV utilities
    Copyright (c) 2005-2011 John Goerzen <jgoerzen@complete.org>

    All rights reserved.

    For license and copyright information, see the file LICENSE
    -}

    {- |
       Module     : Data.CSV
       Copyright  : Copyright (C) 2005-2011 John Goerzen
       SPDX-License-Identifier: BSD-3-Clause

       Stability  : provisional
       Portability: portable

    Haskell Parsec parsers for comma-separated value (CSV) files.

    Written by John Goerzen, jgoerzen\@complete.org
    -}

    module Data.CSV (csvFile, genCsvFile) where
    import Text.ParserCombinators.Parsec
    import Data.List (intersperse)

    eol :: forall st. GenParser Char st String
    eol :: GenParser Char st String
    eol = (GenParser Char st String -> GenParser Char st String
    forall tok st a. GenParser tok st a -> GenParser tok st a
    try (GenParser Char st String -> GenParser Char st String)
    -> GenParser Char st String -> GenParser Char st String
    forall a b. (a -> b) -> a -> b
    $ String -> GenParser Char st String
    forall s (m :: * -> *) u.
    Stream s m Char =>
    String -> ParsecT s u m String
    string "\n\r") GenParser Char st String
    -> GenParser Char st String -> GenParser Char st String
    forall s u (m :: * -> *) a.
    ParsecT s u m a -> ParsecT s u m a -> ParsecT s u m a
    <|> (GenParser Char st String -> GenParser Char st String
    forall tok st a. GenParser tok st a -> GenParser tok st a
    try (GenParser Char st String -> GenParser Char st String)
    -> GenParser Char st String -> GenParser Char st String
    forall a b. (a -> b) -> a -> b
    $ String -> GenParser Char st String
    forall s (m :: * -> *) u.
    Stream s m Char =>
    String -> ParsecT s u m String
    string "\r\n") GenParser Char st String
    -> GenParser Char st String -> GenParser Char st String
    forall s u (m :: * -> *) a.
    ParsecT s u m a -> ParsecT s u m a -> ParsecT s u m a
    <|> String -> GenParser Char st String
    forall s (m :: * -> *) u.
    Stream s m Char =>
    String -> ParsecT s u m String
    string "\n" GenParser Char st String
    -> GenParser Char st String -> GenParser Char st String
    forall s u (m :: * -> *) a.
    ParsecT s u m a -> ParsecT s u m a -> ParsecT s u m a
    <|>
          String -> GenParser Char st String
    forall s (m :: * -> *) u.
    Stream s m Char =>
    String -> ParsecT s u m String
    string "\r" GenParser Char st String -> String -> GenParser Char st String
    forall s u (m :: * -> *) a.
    ParsecT s u m a -> String -> ParsecT s u m a
    <?> "End of line"

    cell :: GenParser Char st String
    cell :: GenParser Char st String
    cell = GenParser Char st String
    forall st. CharParser st String
    quotedcell GenParser Char st String
    -> GenParser Char st String -> GenParser Char st String
    forall s u (m :: * -> *) a.
    ParsecT s u m a -> ParsecT s u m a -> ParsecT s u m a
    <|> ParsecT String st Identity Char -> GenParser Char st String
    forall s u (m :: * -> *) a. ParsecT s u m a -> ParsecT s u m [a]
    many (String -> ParsecT String st Identity Char
    forall s (m :: * -> *) u.
    Stream s m Char =>
    String -> ParsecT s u m Char
    noneOf ",\n\r")

    quotedchar :: GenParser Char st Char
    quotedchar :: GenParser Char st Char
    quotedchar = String -> GenParser Char st Char
    forall s (m :: * -> *) u.
    Stream s m Char =>
    String -> ParsecT s u m Char
    noneOf "\""
                 GenParser Char st Char
    -> GenParser Char st Char -> GenParser Char st Char
    forall s u (m :: * -> *) a.
    ParsecT s u m a -> ParsecT s u m a -> ParsecT s u m a
    <|> (GenParser Char st Char -> GenParser Char st Char
    forall tok st a. GenParser tok st a -> GenParser tok st a
    try (GenParser Char st Char -> GenParser Char st Char)
    -> GenParser Char st Char -> GenParser Char st Char
    forall a b. (a -> b) -> a -> b
    $ do String -> ParsecT String st Identity String
    forall s (m :: * -> *) u.
    Stream s m Char =>
    String -> ParsecT s u m String
    string "\"\""
                               Char -> GenParser Char st Char
    forall (m :: * -> *) a. Monad m => a -> m a
    return '"'
                     )
    quotedcell :: CharParser st String
    quotedcell :: CharParser st String
    quotedcell = do Char -> ParsecT String st Identity Char
    forall s (m :: * -> *) u.
    Stream s m Char =>
    Char -> ParsecT s u m Char
    char '"'
                    String
    content <- ParsecT String st Identity Char -> CharParser st String
    forall s u (m :: * -> *) a. ParsecT s u m a -> ParsecT s u m [a]
    many ParsecT String st Identity Char
    forall st. GenParser Char st Char
    quotedchar
                    Char -> ParsecT String st Identity Char
    forall s (m :: * -> *) u.
    Stream s m Char =>
    Char -> ParsecT s u m Char
    char '"'
                    String -> CharParser st String
    forall (m :: * -> *) a. Monad m => a -> m a
    return String
    content

    line :: GenParser Char st [String]
    line :: GenParser Char st [String]
    line = ParsecT String st Identity String
    -> ParsecT String st Identity Char -> GenParser Char st [String]
    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]
    sepBy ParsecT String st Identity String
    forall st. CharParser st String
    cell (Char -> ParsecT String st Identity Char
    forall s (m :: * -> *) u.
    Stream s m Char =>
    Char -> ParsecT s u m Char
    char ',')

    {- | Parse a Comma-Separated Value (CSV) file.  The return value is a list of
    lines; each line is a list of cells; and each cell is a String.

    Please note that CSV files may have a different number of cells on each line.
    Also, it is impossible to distinguish a CSV line that has a cell with no data
    from a CSV line that has no cells.

    Here are some examples:

    >Input (literal strings)          Parses As (Haskell String syntax)
    >-------------------------------- ---------------------------------
    >1,2,3                            [["1", "2", "3"]]
    >
    >l1                               [["l1"], ["l2"]]
    >l2
    >
    > (empty line)                    [[""]]
    >
    >NQ,"Quoted"                      [["NQ", "Quoted"]]
    >
    >NQ,"Embedded""Quote"             [["NQ", "Embedded\"Quote"]]

    To parse a String, you might use:

    >import Text.ParserCombinators.Parsec
    >import Data.String.CSV
    >....
    >parse csvFile "" mystring

    To parse a file, you might instead use:

    >do result <- parseFromFile csvFile "/path/to/file"

    Please note that the result of parsing will be of type
    (Either ParseError [[String]]).  A Left result indicates an error.
    For more details, see the Parsec information.
    -}

    csvFile :: CharParser st [[String]]
    csvFile :: CharParser st [[String]]
    csvFile = ParsecT String st Identity [String]
    -> ParsecT String st Identity String -> CharParser st [[String]]
    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]
    endBy ParsecT String st Identity [String]
    forall st. GenParser Char st [String]
    line ParsecT String st Identity String
    forall st. CharParser st String
    eol

    {- | Generate CSV data for a file.  The resulting string can be
    written out to disk directly. -}
    genCsvFile :: [[String]] -> String
    genCsvFile :: [[String]] -> String
    genCsvFile inp :: [[String]]
    inp =
        [String] -> String
    unlines ([String] -> String)
    -> ([[String]] -> [String]) -> [[String]] -> String
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . ([String] -> String) -> [[String]] -> [String]
    forall a b. (a -> b) -> [a] -> [b]
    map [String] -> String
    csvline ([[String]] -> String) -> [[String]] -> String
    forall a b. (a -> b) -> a -> b
    $ [[String]]
    inp
        where csvline :: [String] -> String
              csvline :: [String] -> String
    csvline l :: [String]
    l = [String] -> String
    forall (t :: * -> *) a. Foldable t => t [a] -> [a]
    concat ([String] -> String)
    -> ([String] -> [String]) -> [String] -> String
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . String -> [String] -> [String]
    forall a. a -> [a] -> [a]
    intersperse "," ([String] -> [String])
    -> ([String] -> [String]) -> [String] -> [String]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (String -> String) -> [String] -> [String]
    forall a b. (a -> b) -> [a] -> [b]
    map String -> String
    csvcells ([String] -> String) -> [String] -> String
    forall a b. (a -> b) -> a -> b
    $ [String]
    l
              csvcells :: String -> String
              csvcells :: String -> String
    csvcells "" = ""
              csvcells c :: String
    c = '"' Char -> String -> String
    forall a. a -> [a] -> [a]
    : String -> String
    convcell String
    c String -> String -> String
    forall a. [a] -> [a] -> [a]
    ++ "\""
              convcell :: String -> String
              convcell :: String -> String
    convcell c :: String
    c = (Char -> String) -> String -> String
    forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
    concatMap Char -> String
    convchar String
    c
              convchar :: Char -> String
    convchar '"' = "\"\""
              convchar x :: Char
    x = [Char
    x]