{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleInstances #-}
module Coalpit.DSV (showDSV, readDSV) where
import Data.List
import Text.Megaparsec
import Text.Megaparsec.Char
import Data.Void
import Coalpit.Core
composeDSVLine :: Char -> [String] -> String
composeDSVLine fs = intercalate [fs] . map escapeVal
where
escapeVal :: String -> String
escapeVal s = let inner = show s
in if fs `elem` inner
then inner
else init $ tail inner
pStr :: Char -> Parsec Void String String
pStr fs = do
s <- try (between (char '"') (char '"')
(concat <$> many (string "\\\\"
<|> string "\\\""
<|> pure <$> notChar '"')))
<|> many (notChar fs)
case reads (concat ["\"", s, "\""]) of
[(str, "")] -> pure str
other -> fail $ "Failed to read a string: " ++ show other ++ "(" ++ s ++ ")"
pDSVLine :: Char -> Parsec Void String [String]
pDSVLine fs = pStr fs `sepBy` char fs
parseDSVLine :: Char -> String -> Either String [String]
parseDSVLine fs l = case parse (pDSVLine fs) "line" l of
Left err -> Left $ parseErrorPretty err
Right x -> Right x
showDSV :: Coalpit a => Options -> a -> String
showDSV opt = composeDSVLine (fieldSeparator opt) . toArgs opt
readDSV :: Coalpit a => Options -> String -> Either String a
readDSV opt = (>>= fromArgs opt) . parseDSVLine (fieldSeparator opt)