module Text.CSV (CSV
, Record
, Field
, csv
, parseCSV
, parseCSVFromFile
, parseCSVTest
, printCSV
) where
import Text.ParserCombinators.Parsec
import Data.List (intersperse)
type CSV = [Record]
type Record = [Field]
type Field = String
csv :: Parser CSV
csv :: Parser CSV
csv = do CSV
x <- Parser Record
record Parser Record -> ParsecT String () Identity String -> Parser CSV
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 String () Identity Char
-> ParsecT String () Identity String
forall s (m :: * -> *) t u a.
Stream s m t =>
ParsecT s u m a -> ParsecT s u m [a]
many1 (String -> ParsecT String () Identity Char
forall s (m :: * -> *) u.
Stream s m Char =>
String -> ParsecT s u m Char
oneOf String
"\n\r")
ParsecT String () Identity ()
forall s (m :: * -> *) t u.
(Stream s m t, Show t) =>
ParsecT s u m ()
eof
CSV -> Parser CSV
forall (m :: * -> *) a. Monad m => a -> m a
return CSV
x
record :: Parser Record
record :: Parser Record
record = (ParsecT String () Identity String
quotedField ParsecT String () Identity String
-> ParsecT String () Identity String
-> ParsecT String () Identity String
forall s u (m :: * -> *) a.
ParsecT s u m a -> ParsecT s u m a -> ParsecT s u m a
<|> ParsecT String () Identity String
field) ParsecT String () Identity String
-> ParsecT String () Identity Char -> Parser Record
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` Char -> ParsecT String () Identity Char
forall s (m :: * -> *) u.
Stream s m Char =>
Char -> ParsecT s u m Char
char Char
','
field :: Parser Field
field :: ParsecT String () Identity String
field = ParsecT String () Identity Char
-> ParsecT String () Identity String
forall s u (m :: * -> *) a. ParsecT s u m a -> ParsecT s u m [a]
many (String -> ParsecT String () Identity Char
forall s (m :: * -> *) u.
Stream s m Char =>
String -> ParsecT s u m Char
noneOf String
",\n\r\"")
quotedField :: Parser Field
quotedField :: ParsecT String () Identity String
quotedField = ParsecT String () Identity Char
-> ParsecT String () Identity Char
-> ParsecT String () Identity String
-> ParsecT String () Identity String
forall s (m :: * -> *) t u open close a.
Stream s m t =>
ParsecT s u m open
-> ParsecT s u m close -> ParsecT s u m a -> ParsecT s u m a
between (Char -> ParsecT String () Identity Char
forall s (m :: * -> *) u.
Stream s m Char =>
Char -> ParsecT s u m Char
char Char
'"') (Char -> ParsecT String () Identity Char
forall s (m :: * -> *) u.
Stream s m Char =>
Char -> ParsecT s u m Char
char Char
'"') (ParsecT String () Identity String
-> ParsecT String () Identity String)
-> ParsecT String () Identity String
-> ParsecT String () Identity String
forall a b. (a -> b) -> a -> b
$
ParsecT String () Identity Char
-> ParsecT String () Identity String
forall s u (m :: * -> *) a. ParsecT s u m a -> ParsecT s u m [a]
many (String -> ParsecT String () Identity Char
forall s (m :: * -> *) u.
Stream s m Char =>
String -> ParsecT s u m Char
noneOf String
"\"" ParsecT String () Identity Char
-> ParsecT String () Identity Char
-> ParsecT String () Identity Char
forall s u (m :: * -> *) a.
ParsecT s u m a -> ParsecT s u m a -> ParsecT s u m a
<|> ParsecT String () Identity Char -> ParsecT String () Identity Char
forall tok st a. GenParser tok st a -> GenParser tok st a
try (String -> ParsecT String () Identity String
forall s (m :: * -> *) u.
Stream s m Char =>
String -> ParsecT s u m String
string String
"\"\"" ParsecT String () Identity String
-> ParsecT String () Identity Char
-> ParsecT String () Identity Char
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Char -> ParsecT String () Identity Char
forall (m :: * -> *) a. Monad m => a -> m a
return Char
'"'))
parseCSV :: FilePath -> String -> Either ParseError CSV
parseCSV :: String -> String -> Either ParseError CSV
parseCSV = Parser CSV -> String -> String -> Either ParseError CSV
forall s t a.
Stream s Identity t =>
Parsec s () a -> String -> s -> Either ParseError a
parse Parser CSV
csv
parseCSVFromFile :: FilePath -> IO (Either ParseError CSV)
parseCSVFromFile :: String -> IO (Either ParseError CSV)
parseCSVFromFile = Parser CSV -> String -> IO (Either ParseError CSV)
forall a. Parser a -> String -> IO (Either ParseError a)
parseFromFile Parser CSV
csv
parseCSVTest :: String -> IO ()
parseCSVTest :: String -> IO ()
parseCSVTest = Parser CSV -> String -> IO ()
forall s t a.
(Stream s Identity t, Show a) =>
Parsec s () a -> s -> IO ()
parseTest Parser CSV
csv
printCSV :: CSV -> String
printCSV :: CSV -> String
printCSV CSV
records = Record -> String
unlines (Record -> String
printRecord (Record -> String) -> CSV -> Record
forall a b. (a -> b) -> [a] -> [b]
`map` CSV
records)
where printRecord :: Record -> String
printRecord = Record -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat (Record -> String) -> (Record -> Record) -> Record -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Record -> Record
forall a. a -> [a] -> [a]
intersperse String
"," (Record -> Record) -> (Record -> Record) -> Record -> Record
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String -> String) -> Record -> Record
forall a b. (a -> b) -> [a] -> [b]
map String -> String
forall (t :: * -> *). Foldable t => t Char -> String
printField
printField :: t Char -> String
printField t Char
f = String
"\"" String -> String -> String
forall a. [a] -> [a] -> [a]
++ (Char -> String) -> t Char -> String
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Char -> String
escape t Char
f String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"\""
escape :: Char -> String
escape Char
'"' = String
"\"\""
escape Char
x = [Char
x]
unlines :: Record -> String
unlines = Record -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat (Record -> String) -> (Record -> Record) -> Record -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Record -> Record
forall a. a -> [a] -> [a]
intersperse String
"\n"