{-|

  This module exports the underlying Attoparsec row parser. This is helpful if
  you want to do some ad-hoc CSV string parsing.

-}

module Data.CSV.Conduit.Parser.ByteString
    ( parseCSV
    , parseRow
    , row
    , csv
    ) where

-------------------------------------------------------------------------------
import           Control.Applicative
import           Control.Monad                    (mzero)
import           Data.Attoparsec.ByteString       as P hiding (take)
import qualified Data.Attoparsec.ByteString.Char8 as C8
import           Data.ByteString.Char8            (ByteString)
import qualified Data.ByteString.Char8            as B8
import           Data.Word                        (Word8)
-------------------------------------------------------------------------------
import           Data.CSV.Conduit.Types


------------------------------------------------------------------------------
-- | Try to parse given string as CSV
parseCSV :: CSVSettings -> ByteString -> Either String [Row ByteString]
parseCSV :: CSVSettings -> ByteString -> Either String [Row ByteString]
parseCSV CSVSettings
s = Parser [Row ByteString]
-> ByteString -> Either String [Row ByteString]
forall a. Parser a -> ByteString -> Either String a
parseOnly (Parser [Row ByteString]
 -> ByteString -> Either String [Row ByteString])
-> Parser [Row ByteString]
-> ByteString
-> Either String [Row ByteString]
forall a b. (a -> b) -> a -> b
$ CSVSettings -> Parser [Row ByteString]
csv CSVSettings
s


------------------------------------------------------------------------------
-- | Try to parse given string as 'Row ByteString'
parseRow :: CSVSettings -> ByteString -> Either String (Maybe (Row ByteString))
parseRow :: CSVSettings -> ByteString -> Either String (Maybe (Row ByteString))
parseRow CSVSettings
s = Parser (Maybe (Row ByteString))
-> ByteString -> Either String (Maybe (Row ByteString))
forall a. Parser a -> ByteString -> Either String a
parseOnly (Parser (Maybe (Row ByteString))
 -> ByteString -> Either String (Maybe (Row ByteString)))
-> Parser (Maybe (Row ByteString))
-> ByteString
-> Either String (Maybe (Row ByteString))
forall a b. (a -> b) -> a -> b
$ CSVSettings -> Parser (Maybe (Row ByteString))
row CSVSettings
s


------------------------------------------------------------------------------
-- | Parse CSV
csv :: CSVSettings -> Parser [Row ByteString]
csv :: CSVSettings -> Parser [Row ByteString]
csv CSVSettings
s = do
  Maybe (Row ByteString)
r <- CSVSettings -> Parser (Maybe (Row ByteString))
row CSVSettings
s
  Bool
end <- Parser ByteString Bool
forall t. Chunk t => Parser t Bool
atEnd
  if Bool
end
    then case Maybe (Row ByteString)
r of
      Just Row ByteString
x -> [Row ByteString] -> Parser [Row ByteString]
forall (m :: * -> *) a. Monad m => a -> m a
return [Row ByteString
x]
      Maybe (Row ByteString)
Nothing -> [Row ByteString] -> Parser [Row ByteString]
forall (m :: * -> *) a. Monad m => a -> m a
return []
    else do
      [Row ByteString]
rest <- CSVSettings -> Parser [Row ByteString]
csv CSVSettings
s
      [Row ByteString] -> Parser [Row ByteString]
forall (m :: * -> *) a. Monad m => a -> m a
return ([Row ByteString] -> Parser [Row ByteString])
-> [Row ByteString] -> Parser [Row ByteString]
forall a b. (a -> b) -> a -> b
$ case Maybe (Row ByteString)
r of
        Just Row ByteString
x -> Row ByteString
x Row ByteString -> [Row ByteString] -> [Row ByteString]
forall a. a -> [a] -> [a]
: [Row ByteString]
rest
        Maybe (Row ByteString)
Nothing -> [Row ByteString]
rest


------------------------------------------------------------------------------
-- | Parse a CSV row
row :: CSVSettings -> Parser (Maybe (Row ByteString))
row :: CSVSettings -> Parser (Maybe (Row ByteString))
row CSVSettings
csvs = CSVSettings -> Parser (Maybe (Row ByteString))
csvrow CSVSettings
csvs Parser (Maybe (Row ByteString))
-> Parser (Maybe (Row ByteString))
-> Parser (Maybe (Row ByteString))
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Parser (Maybe (Row ByteString))
badrow


badrow :: Parser (Maybe (Row ByteString))
badrow :: Parser (Maybe (Row ByteString))
badrow = (Word8 -> Bool) -> Parser ByteString
P.takeWhile (Bool -> Bool
not (Bool -> Bool) -> (Word8 -> Bool) -> Word8 -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word8 -> Bool
C8.isEndOfLine) Parser ByteString -> Parser ByteString () -> Parser ByteString ()
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*>
         (Parser ByteString ()
C8.endOfLine Parser ByteString ()
-> Parser ByteString () -> Parser ByteString ()
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Parser ByteString ()
forall t. Chunk t => Parser t ()
C8.endOfInput) Parser ByteString ()
-> Parser (Maybe (Row ByteString))
-> Parser (Maybe (Row ByteString))
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> Maybe (Row ByteString) -> Parser (Maybe (Row ByteString))
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe (Row ByteString)
forall a. Maybe a
Nothing

csvrow :: CSVSettings -> Parser (Maybe (Row ByteString))
csvrow :: CSVSettings -> Parser (Maybe (Row ByteString))
csvrow CSVSettings
c =
  let rowbody :: Parser ByteString (Row ByteString)
rowbody = (Parser ByteString
quotedField' Parser ByteString -> Parser ByteString -> Parser ByteString
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> CSVSettings -> Parser ByteString
field CSVSettings
c) Parser ByteString
-> Parser ByteString Char -> Parser ByteString (Row ByteString)
forall (f :: * -> *) a s. Alternative f => f a -> f s -> f [a]
`sepBy` Char -> Parser ByteString Char
C8.char (CSVSettings -> Char
csvSep CSVSettings
c)
      properrow :: Parser ByteString (Row ByteString)
properrow = Parser ByteString (Row ByteString)
rowbody Parser ByteString (Row ByteString)
-> Parser ByteString () -> Parser ByteString (Row ByteString)
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* (Parser ByteString ()
C8.endOfLine Parser ByteString ()
-> Parser ByteString () -> Parser ByteString ()
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Parser ByteString ()
forall t. Chunk t => Parser t ()
P.endOfInput)
      quotedField' :: Parser ByteString
quotedField' = case CSVSettings -> Maybe Char
csvQuoteChar CSVSettings
c of
          Maybe Char
Nothing -> Parser ByteString
forall (m :: * -> *) a. MonadPlus m => m a
mzero
          Just Char
q' -> Parser ByteString -> Parser ByteString
forall i a. Parser i a -> Parser i a
try (Char -> Parser ByteString
quotedField Char
q')
  in do
    Row ByteString
res <- Parser ByteString (Row ByteString)
properrow
    Maybe (Row ByteString) -> Parser (Maybe (Row ByteString))
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe (Row ByteString) -> Parser (Maybe (Row ByteString)))
-> Maybe (Row ByteString) -> Parser (Maybe (Row ByteString))
forall a b. (a -> b) -> a -> b
$ Row ByteString -> Maybe (Row ByteString)
forall a. a -> Maybe a
Just Row ByteString
res

field :: CSVSettings -> Parser ByteString
field :: CSVSettings -> Parser ByteString
field CSVSettings
s = (Word8 -> Bool) -> Parser ByteString
P.takeWhile (CSVSettings -> Word8 -> Bool
isFieldChar CSVSettings
s)

isFieldChar :: CSVSettings -> Word8 -> Bool
isFieldChar :: CSVSettings -> Word8 -> Bool
isFieldChar CSVSettings
s = String -> Word8 -> Bool
notInClass String
xs'
  where xs :: String
xs = CSVSettings -> Char
csvSep CSVSettings
s Char -> String -> String
forall a. a -> [a] -> [a]
: String
"\n\r"
        xs' :: String
xs' = case CSVSettings -> Maybe Char
csvQuoteChar CSVSettings
s of
          Maybe Char
Nothing -> String
xs
          Just Char
x -> Char
x Char -> String -> String
forall a. a -> [a] -> [a]
: String
xs

quotedField :: Char -> Parser ByteString
quotedField :: Char -> Parser ByteString
quotedField Char
c =
  let quoted :: Parser ByteString Char
quoted = ByteString -> Parser ByteString
string ByteString
dbl Parser ByteString
-> Parser ByteString Char -> Parser ByteString Char
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> Char -> Parser ByteString Char
forall (m :: * -> *) a. Monad m => a -> m a
return Char
c
      dbl :: ByteString
dbl = String -> ByteString
B8.pack [Char
c,Char
c]
  in do
  Char
_ <- Char -> Parser ByteString Char
C8.char Char
c
  String
f <- Parser ByteString Char -> Parser ByteString String
forall (f :: * -> *) a. Alternative f => f a -> f [a]
many (Char -> Parser ByteString Char
C8.notChar Char
c Parser ByteString Char
-> Parser ByteString Char -> Parser ByteString Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Parser ByteString Char
quoted)
  Char
_ <- Char -> Parser ByteString Char
C8.char Char
c
  ByteString -> Parser ByteString
forall (m :: * -> *) a. Monad m => a -> m a
return (ByteString -> Parser ByteString)
-> ByteString -> Parser ByteString
forall a b. (a -> b) -> a -> b
$ String -> ByteString
B8.pack String
f