on master help sign in
CSV round-trip with Cons and escaped characters
commit1805b77cf7f6de18fd8485b26f205aa9757907ec authorAgentM <agentm@themactionfaction.com> dateFri 22 Sep 2017, 18:42 (9 years ago) parent58e2c65
2 files changed+25−17
src/lib/ProjectM36/Relation/Parse/CSV.hs+16−12 test/Relation/Export/CSV.hs+9−5
src/lib/ProjectM36/Relation/Parse/CSV.hs+16−12
@@ -12,7 +12,7 @@ import Data.Char (ord, isUpper, isSpace)
1212import qualified Data.ByteString.Lazy as BS
1313import Data.Text.Encoding (decodeUtf8)
1414import qualified Data.Set as S
15−import Data.HashMap.Lazy as HM
15+import qualified Data.HashMap.Lazy as HM
1616import qualified Data.List as L
1717import qualified Data.Text as T
1818import qualified Data.Attoparsec.ByteString.Lazy as APBL
@@ -60,9 +60,9 @@ parseCSVAtomP :: AttributeName -> TypeConstructorMapping -> AtomType -> APT.Pars
6060parseCSVAtomP _ _ IntegerAtomType = (Right . IntegerAtom) <$> APT.decimal
6161parseCSVAtomP _ _ IntAtomType = Right . IntAtom <$> APT.decimal
6262parseCSVAtomP _ _ DoubleAtomType = Right . DoubleAtom <$> APT.double
63−parseCSVAtomP _ _ TextAtomType = Right . TextAtom <$>
64− (quotedString <|>
65− takeToEndOfData)
63+parseCSVAtomP _ _ TextAtomType = do
64+ s <- quotedString <|> takeToEndOfData
65+ pure (Right (TextAtom s))
6666parseCSVAtomP _ _ DayAtomType = do
6767 dString <- T.unpack <$> takeToEndOfData
6868 case readMaybe dString of
@@ -142,14 +142,18 @@ parens p = do
142142
143143quotedString :: APT.Parser T.Text
144144quotedString = do
145+ let escapeMap = [('"','"'), ('n', '\n'), ('r', '\r')]
145146 APT.skip (== '"')
146− v <- APT.scan False (\prevEscape nextChar -> if nextChar == '"' && not prevEscape then
147− Nothing
148− else if nextChar == '\\' then
149− Just True
150− else
151− Just False
152− )
147+ (_, s) <- APT.runScanner [] (\prevl nextChar -> case prevl of
148+ [] -> Just [nextChar]
149+ chars -> if last chars == '\\' then
150+ case lookup nextChar escapeMap of
151+ Nothing -> Just (chars ++ [nextChar]) --there is no escape sequence, so leave backslash + char
152+ Just escapeVal -> Just ((init chars) ++ [escapeVal]) -- nuke the backslash and add the escapeVal
153+ else if nextChar == '"' then
154+ Nothing
155+ else
156+ Just (chars ++ [nextChar]))
153157 APT.skip (== '"')
154− pure v
158+ pure (T.pack s)
155159
test/Relation/Export/CSV.hs+9−5
@@ -12,9 +12,12 @@ import Data.Time.Calendar
1212import Data.Time.Clock
1313import Data.Monoid
1414
15−--import qualified Data.Text.Lazy as TL
16−--import qualified Data.Text.Lazy.Encoding as TE
17−--import ProjectM36.Relation.Show.Term
15+{-
16+import qualified Data.Text.Lazy as TL
17+import qualified Data.Text.Lazy.Encoding as TE
18+import ProjectM36.Relation.Show.Term
19+import qualified Data.ByteString.Lazy as BS
20+-}
1821
1922main :: IO ()
2023main = do
@@ -52,7 +55,7 @@ testCSVExport = TestCase $ do
5255 Attribute "dayattr" DayAtomType,
5356 Attribute "datetimeattr" DateTimeAtomType,
5457 Attribute "listintegerattr" (listAtomType IntegerAtomType),
55− Attribute "textintegerattr" (listAtomType TextAtomType),
58+ Attribute "listtextattr" (listAtomType TextAtomType),
5659 Attribute "intervalattr" (IntervalAtomType DateTimeAtomType)
5760 ]
5861 relOrErr = mkRelationFromList attrs [
@@ -69,7 +72,7 @@ testCSVExport = TestCase $ do
6972 DayAtom (fromGregorian 1001 6 28),
7073 DateTimeAtom (addUTCTime 360 now),
7174 listCons IntegerAtomType [IntegerAtom 10, IntegerAtom 11, IntegerAtom 12],
72− listCons TextAtomType [TextAtom "text5", TextAtom "text6"],
75+ listCons TextAtomType [TextAtom "text5\"", TextAtom "text6\r\n"],
7376 testInterval
7477 ]]
7578
@@ -79,6 +82,7 @@ testCSVExport = TestCase $ do
7982 case relationAsCSV rel of
8083 Left err -> assertFailure $ "export failed: " ++ show err
8184 Right csvData ->
85+ --BS.writeFile "/tmp/csv" csvData
8286 --putStrLn (TL.unpack (TE.decodeUtf8 csvData))
8387 case csvAsRelation attrs basicTypeConstructorMapping csvData of -- import csv data back to relation
8488 Left err -> assertFailure $ "re-import failed: " ++ show err