content: {-|
  
    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.Text
      ( parseCSV
      , parseRow
      , row
      , csv
      ) where
  
  -------------------------------------------------------------------------------
  import           Control.Applicative
  import           Control.Monad          (mzero)
  import           Data.Attoparsec.Text   as P hiding (take)
  import qualified Data.Attoparsec.Text   as T
  import           Data.Text              (Text)
  import qualified Data.Text              as T
  -------------------------------------------------------------------------------
  import           Data.CSV.Conduit.Types
  -------------------------------------------------------------------------------
  
  
  ------------------------------------------------------------------------------
  -- | Try to parse given string as CSV
  parseCSV :: CSVSettings -> Text -> Either String [Row Text]
  parseCSV s = parseOnly $ csv s
  
  
  ------------------------------------------------------------------------------
  -- | Try to parse given string as 'Row Text'
  parseRow :: CSVSettings -> Text -> Either String (Maybe (Row Text))
  parseRow s = parseOnly $ row s
  
  
  ------------------------------------------------------------------------------
  -- | Parse CSV
  csv :: CSVSettings -> Parser [Row Text]
  csv s = do
    r <- row s
    end <- atEnd
    if end
      then case r of
        Just x -> return [x]
        Nothing -> return []
      else do
        rest <- csv s
        return $ case r of
          Just x -> x : rest
          Nothing -> rest
  
  
  ------------------------------------------------------------------------------
  -- | Parse a CSV row
  row :: CSVSettings -> Parser (Maybe (Row Text))
  row csvs = csvrow csvs <|> badrow
  
  csvEndOfLine :: Parser ()
  csvEndOfLine = (char '\n' >> return ()) <|> (string (T.pack "\r\n") >> return ()) <|> (char '\r' >> return ())
  
  badrow :: Parser (Maybe (Row Text))
  badrow = P.takeWhile (not . T.isEndOfLine) *>
           (csvEndOfLine <|> T.endOfInput) *> return Nothing
  
  csvrow :: CSVSettings -> Parser (Maybe (Row Text))
  csvrow c =
    let rowbody = (quotedField' <|> field c) `sepBy` T.char (csvSep c)
        properrow = rowbody <* (csvEndOfLine <|> P.endOfInput)
        quotedField' = case csvQuoteChar c of
            Nothing -> mzero
            Just q' -> try (quotedField q')
    in do
      res <- properrow
      return $ Just res
  
  field :: CSVSettings -> Parser Text
  field s = P.takeWhile (isFieldChar s)
  
  isFieldChar :: CSVSettings -> Char -> Bool
  isFieldChar s = notInClass xs'
    where xs = csvSep s : "\n\r"
          xs' = case csvQuoteChar s of
            Nothing -> xs
            Just x -> x : xs
  
  quotedField :: Char -> Parser Text
  quotedField c = do
    let quoted = string dbl *> return c
        dbl = T.pack [c,c]
    _ <- T.char c
    f <- many (T.notChar c <|> quoted)
    _ <- T.char c
    return $ T.pack f
  
  
  