packages feed

csv-conduit 1.0.0.1 → 1.0.1.0

raw patch · 9 files changed

+67/−34 lines, 9 filesdep ~conduitbinary-addedPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: conduit

API changes (from Hackage documentation)

- Data.CSV.Conduit: instance (Data.CSV.Conduit.CSV s (Data.CSV.Conduit.Types.Row s'), GHC.Classes.Ord s', Data.String.IsString s) => Data.CSV.Conduit.CSV s (Data.CSV.Conduit.Types.MapRow s')
- Data.CSV.Conduit: instance (Data.CSV.Conduit.CSV s (Data.CSV.Conduit.Types.Row s'), GHC.Classes.Ord s', Data.String.IsString s) => Data.CSV.Conduit.CSV s (Data.CSV.Conduit.Types.OrderedMapRow s')
- Data.CSV.Conduit: instance Data.CSV.Conduit.CSV Data.ByteString.Internal.Type.ByteString (Data.CSV.Conduit.Types.Row GHC.Base.String)
- Data.CSV.Conduit.Conversion: instance Control.Monad.Fail.MonadFail Data.CSV.Conduit.Conversion.Parser
- Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.FromField GHC.Int.Int16
- Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.FromField GHC.Int.Int32
- Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.FromField GHC.Int.Int64
- Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.FromField GHC.Int.Int8
- Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.FromField GHC.Word.Word16
- Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.FromField GHC.Word.Word32
- Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.FromField GHC.Word.Word64
- Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.FromField GHC.Word.Word8
- Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.FromField a => Data.CSV.Conduit.Conversion.FromField (GHC.Maybe.Maybe a)
- Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.ToField GHC.Int.Int16
- Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.ToField GHC.Int.Int32
- Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.ToField GHC.Int.Int64
- Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.ToField GHC.Int.Int8
- Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.ToField GHC.Word.Word16
- Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.ToField GHC.Word.Word32
- Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.ToField GHC.Word.Word64
- Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.ToField GHC.Word.Word8
- Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.ToField a => Data.CSV.Conduit.Conversion.ToField (GHC.Maybe.Maybe a)
- Data.CSV.Conduit.Conversion: instance GHC.Base.Alternative Data.CSV.Conduit.Conversion.Parser
- Data.CSV.Conduit.Conversion: instance GHC.Base.Applicative Data.CSV.Conduit.Conversion.Parser
- Data.CSV.Conduit.Conversion: instance GHC.Base.Functor Data.CSV.Conduit.Conversion.Parser
- Data.CSV.Conduit.Conversion: instance GHC.Base.Monad Data.CSV.Conduit.Conversion.Parser
- Data.CSV.Conduit.Conversion: instance GHC.Base.MonadPlus Data.CSV.Conduit.Conversion.Parser
- Data.CSV.Conduit.Conversion: instance GHC.Base.Semigroup (Data.CSV.Conduit.Conversion.Parser a)
- Data.CSV.Conduit.Conversion: instance GHC.Read.Read a => GHC.Read.Read (Data.CSV.Conduit.Conversion.Named a)
- Data.CSV.Conduit.Conversion: instance GHC.Read.Read a => GHC.Read.Read (Data.CSV.Conduit.Conversion.NamedOrdered a)
- Data.CSV.Conduit.Conversion: instance GHC.Read.Read a => GHC.Read.Read (Data.CSV.Conduit.Conversion.Only a)
- Data.CSV.Conduit.Conversion: instance GHC.Show.Show a => GHC.Show.Show (Data.CSV.Conduit.Conversion.Named a)
- Data.CSV.Conduit.Conversion: instance GHC.Show.Show a => GHC.Show.Show (Data.CSV.Conduit.Conversion.NamedOrdered a)
- Data.CSV.Conduit.Conversion: instance GHC.Show.Show a => GHC.Show.Show (Data.CSV.Conduit.Conversion.Only a)
- Data.CSV.Conduit.Types: instance GHC.Show.Show Data.CSV.Conduit.Types.CSVSettings
- Data.CSV.Conduit.Types: instance GHC.Show.Show Data.CSV.Conduit.Types.QuoteEmpty
+ Data.CSV.Conduit: instance (Data.CSV.Conduit.CSV s (Data.CSV.Conduit.Types.Row s'), GHC.Classes.Ord s', GHC.Internal.Data.String.IsString s) => Data.CSV.Conduit.CSV s (Data.CSV.Conduit.Types.MapRow s')
+ Data.CSV.Conduit: instance (Data.CSV.Conduit.CSV s (Data.CSV.Conduit.Types.Row s'), GHC.Classes.Ord s', GHC.Internal.Data.String.IsString s) => Data.CSV.Conduit.CSV s (Data.CSV.Conduit.Types.OrderedMapRow s')
+ Data.CSV.Conduit: instance Data.CSV.Conduit.CSV Data.ByteString.Internal.Type.ByteString (Data.CSV.Conduit.Types.Row GHC.Internal.Base.String)
+ Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.FromField GHC.Internal.Int.Int16
+ Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.FromField GHC.Internal.Int.Int32
+ Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.FromField GHC.Internal.Int.Int64
+ Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.FromField GHC.Internal.Int.Int8
+ Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.FromField GHC.Internal.Word.Word16
+ Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.FromField GHC.Internal.Word.Word32
+ Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.FromField GHC.Internal.Word.Word64
+ Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.FromField GHC.Internal.Word.Word8
+ Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.FromField a => Data.CSV.Conduit.Conversion.FromField (GHC.Internal.Maybe.Maybe a)
+ Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.ToField GHC.Internal.Int.Int16
+ Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.ToField GHC.Internal.Int.Int32
+ Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.ToField GHC.Internal.Int.Int64
+ Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.ToField GHC.Internal.Int.Int8
+ Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.ToField GHC.Internal.Word.Word16
+ Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.ToField GHC.Internal.Word.Word32
+ Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.ToField GHC.Internal.Word.Word64
+ Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.ToField GHC.Internal.Word.Word8
+ Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.ToField a => Data.CSV.Conduit.Conversion.ToField (GHC.Internal.Maybe.Maybe a)
+ Data.CSV.Conduit.Conversion: instance GHC.Internal.Base.Alternative Data.CSV.Conduit.Conversion.Parser
+ Data.CSV.Conduit.Conversion: instance GHC.Internal.Base.Applicative Data.CSV.Conduit.Conversion.Parser
+ Data.CSV.Conduit.Conversion: instance GHC.Internal.Base.Functor Data.CSV.Conduit.Conversion.Parser
+ Data.CSV.Conduit.Conversion: instance GHC.Internal.Base.Monad Data.CSV.Conduit.Conversion.Parser
+ Data.CSV.Conduit.Conversion: instance GHC.Internal.Base.MonadPlus Data.CSV.Conduit.Conversion.Parser
+ Data.CSV.Conduit.Conversion: instance GHC.Internal.Base.Semigroup (Data.CSV.Conduit.Conversion.Parser a)
+ Data.CSV.Conduit.Conversion: instance GHC.Internal.Control.Monad.Fail.MonadFail Data.CSV.Conduit.Conversion.Parser
+ Data.CSV.Conduit.Conversion: instance GHC.Internal.Read.Read a => GHC.Internal.Read.Read (Data.CSV.Conduit.Conversion.Named a)
+ Data.CSV.Conduit.Conversion: instance GHC.Internal.Read.Read a => GHC.Internal.Read.Read (Data.CSV.Conduit.Conversion.NamedOrdered a)
+ Data.CSV.Conduit.Conversion: instance GHC.Internal.Read.Read a => GHC.Internal.Read.Read (Data.CSV.Conduit.Conversion.Only a)
+ Data.CSV.Conduit.Conversion: instance GHC.Internal.Show.Show a => GHC.Internal.Show.Show (Data.CSV.Conduit.Conversion.Named a)
+ Data.CSV.Conduit.Conversion: instance GHC.Internal.Show.Show a => GHC.Internal.Show.Show (Data.CSV.Conduit.Conversion.NamedOrdered a)
+ Data.CSV.Conduit.Conversion: instance GHC.Internal.Show.Show a => GHC.Internal.Show.Show (Data.CSV.Conduit.Conversion.Only a)
+ Data.CSV.Conduit.Types: instance GHC.Internal.Show.Show Data.CSV.Conduit.Types.CSVSettings
+ Data.CSV.Conduit.Types: instance GHC.Internal.Show.Show Data.CSV.Conduit.Types.QuoteEmpty
- Data.CSV.Conduit: decodeCSV :: forall v a s. (Vector v a, CSV s a) => CSVSettings -> s -> Either SomeException (v a)
+ Data.CSV.Conduit: decodeCSV :: (Vector v a, CSV s a) => CSVSettings -> s -> Either SomeException (v a)
- Data.CSV.Conduit: fromCSV :: (CSV s r, Monad m) => CSVSettings -> ConduitM r s m ()
+ Data.CSV.Conduit: fromCSV :: forall (m :: Type -> Type). (CSV s r, Monad m) => CSVSettings -> ConduitT r s m ()
- Data.CSV.Conduit: intoCSV :: (CSV s r, MonadThrow m) => CSVSettings -> ConduitM s r m ()
+ Data.CSV.Conduit: intoCSV :: forall (m :: Type -> Type). (CSV s r, MonadThrow m) => CSVSettings -> ConduitT s r m ()
- Data.CSV.Conduit: transformCSV :: (MonadThrow m, CSV s a, CSV s' b) => CSVSettings -> ConduitM () s m () -> ConduitM a b m () -> ConduitM s' Void m () -> m ()
+ Data.CSV.Conduit: transformCSV :: (MonadThrow m, CSV s a, CSV s' b) => CSVSettings -> ConduitT () s m () -> ConduitT a b m () -> ConduitT s' Void m () -> m ()
- Data.CSV.Conduit: transformCSV' :: (MonadThrow m, CSV s a, CSV s' b) => CSVSettings -> CSVSettings -> ConduitM () s m () -> ConduitM a b m () -> ConduitM s' Void m () -> m ()
+ Data.CSV.Conduit: transformCSV' :: (MonadThrow m, CSV s a, CSV s' b) => CSVSettings -> CSVSettings -> ConduitT () s m () -> ConduitT a b m () -> ConduitT s' Void m () -> m ()
- Data.CSV.Conduit: writeHeaders :: (Monad m, CSV s (Row r), IsString s) => CSVSettings -> ConduitM (MapRow r) s m ()
+ Data.CSV.Conduit: writeHeaders :: forall (m :: Type -> Type) s r. (Monad m, CSV s (Row r), IsString s) => CSVSettings -> ConduitT (MapRow r) s m ()
- Data.CSV.Conduit: writeHeadersOrdered :: (Monad m, CSV s (Row r), IsString s) => CSVSettings -> ConduitM (OrderedMapRow r) s m ()
+ Data.CSV.Conduit: writeHeadersOrdered :: forall (m :: Type -> Type) s r. (Monad m, CSV s (Row r), IsString s) => CSVSettings -> ConduitM (OrderedMapRow r) s m ()

Files

README.md view
@@ -1,7 +1,7 @@ # README [![cabal-build](https://github.com/dmvianna/csv-conduit/actions/workflows/cabal.yml/badge.svg)](https://github.com/dmvianna/csv-conduit/actions)-[![stack build](https://github.com/dmvianna/csv-conduit/actions/workflows/stack.yml/badge.svg)](https://github.com/dmvianna/csv-conduit/actions)+build](https://github.com/ozataman/csv-conduit/actions/workflows/cabal.yml/badge.svg)](https://github.com/ozataman/csv-conduit/actions)+[![stack build](https://github.com/ozataman/csv-conduit/actions/workflows/stack.yml/badge.svg)](https://github.com/ozataman/csv-conduit/actions)  ## CSV Files and Haskell @@ -53,7 +53,7 @@ - Dmitry Dzhus (@dzhus) - Niklas Hambüchen (@nh2) - Facundo Domínguez (@facundominguez)-+- Daniel Vianna (@dmvianna)  ### Introduction 
changelog.md view
@@ -1,3 +1,11 @@+1.0.1.0+* Use ConduitT instead of ConduitM (prettier type inference with newer conduit imports)++1.0.0.2+* Fixed [#17](https://github.com/ozataman/csv-conduit/issues/17),+  where CSV created with Excel in Mac OS failed to parse due to its+  newline characters.+ 1.0.0.1 * Removed dependencies: mmorph, monad-control, mtl,   unordered-containers, primitive
csv-conduit.cabal view
@@ -1,5 +1,5 @@ name:               csv-conduit-version:            1.0.0.1+version:            1.0.1.0 synopsis:   A flexible, fast, conduit-based CSV parser library for Haskell. @@ -57,8 +57,10 @@ extra-source-files:   changelog.md   README.md+  test/test-mac-excel.csv   test/test.csv   test/Test.hs+  test/test.xls  flag lib-Werror   default: False@@ -89,7 +91,7 @@     , base                >=4       && <5     , blaze-builder     , bytestring-    , conduit             >=1.2.8+    , conduit             >=1.3.0   && <2.0     , conduit-extra     , containers          >=0.3     , data-default
src/Data/CSV/Conduit.hs view
@@ -135,13 +135,13 @@   -----------------------------------------------------------------------------   -- | Turn a stream of 's' into a stream of CSV row type. An example   -- would be parsing a ByteString stream as rows of 'MapRow' 'Text'.-  intoCSV :: (MonadThrow m) => CSVSettings -> ConduitM s r m ()+  intoCSV :: (MonadThrow m) => CSVSettings -> ConduitT s r m ()    -----------------------------------------------------------------------------   -- | Turn a stream of CSV row type back into a stream of 's'. An   -- example would be rendering a stream of 'Row' 'ByteString' rows as   -- 'Text'.-  fromCSV :: Monad m => CSVSettings -> ConduitM r s m ()+  fromCSV :: Monad m => CSVSettings -> ConduitT r s m ()   @@ -213,13 +213,13 @@  ------------------------------------------------------------------------------- fromCSVRow :: (Monad m, IsString s, CSV s r)-           => CSVSettings -> ConduitM r s m ()+           => CSVSettings -> ConduitT r s m () fromCSVRow set = awaitForever $ \row -> mapM_ yield [rowToStr set row, "\n"]    --------------------------------------------------------------------------------intoCSVRow :: (MonadThrow m, AttoparsecInput i) => Parser i (Maybe o) -> ConduitM i o m ()+intoCSVRow :: (MonadThrow m, AttoparsecInput i) => Parser i (Maybe o) -> ConduitT i o m () intoCSVRow p = parse .| puller   where     parse = {-# SCC "conduitParser_p" #-} conduitParser p@@ -243,7 +243,7 @@  ------------------------------------------------------------------------------- intoCSVMap :: (Ord a, MonadThrow m, CSV s [a])-           => CSVSettings -> ConduitM s (MapRow a) m ()+           => CSVSettings -> ConduitT s (MapRow a) m () intoCSVMap set = intoCSV set .| (headers >>= converter)   where     headers = do@@ -298,7 +298,7 @@  ------------------------------------------------------------------------------- fromCSVMap :: (Monad m, IsString s, CSV s [a])-           => CSVSettings -> ConduitM (M.Map k a) s m ()+           => CSVSettings -> ConduitT (M.Map k a) s m () fromCSVMap set = awaitForever push   where     push r = mapM_ yield [rowToStr set (M.elems r), "\n"]@@ -321,7 +321,7 @@ writeHeaders     :: (Monad m, CSV s (Row r), IsString s)     => CSVSettings-    -> ConduitM (MapRow r) s m ()+    -> ConduitT (MapRow r) s m () writeHeaders set = do   mrow <- await   case mrow of@@ -380,14 +380,14 @@     -> Either SomeException (v a) decodeCSV set bs = runST $ runExceptT pipeline   where-    src :: ConduitM () s (ExceptT SomeException (ST s1)) ()+    src :: ConduitT () s (ExceptT SomeException (ST s1)) ()     src = C.sourceList [bs]-    csvConvert :: ConduitM s a (ExceptT SomeException (ST s1)) ()+    csvConvert :: ConduitT s a (ExceptT SomeException (ST s1)) ()     csvConvert = transPipe (ExceptT . runCatchT) csvConvert'-    csvConvert' :: ConduitM s a (CatchT (ST s1)) ()+    csvConvert' :: ConduitT s a (CatchT (ST s1)) ()     csvConvert' = intoCSV set     growthFactor = 10-    sink :: ConduitM a Void.Void (ExceptT SomeException (ST s1)) (v a)+    sink :: ConduitT a Void.Void (ExceptT SomeException (ST s1)) (v a)     sink = sinkVector growthFactor     pipeline :: ExceptT SomeException (ST s1) (v a)     pipeline = runConduit (src .| csvConvert .| sink)@@ -447,11 +447,11 @@     :: (MonadThrow m, CSV s a, CSV s' b)     => CSVSettings     -- ^ Settings to be used for both input and output-    -> ConduitM () s m ()+    -> ConduitT () s m ()     -- ^ A raw stream data source. Ex: 'sourceFile inFile'-    -> ConduitM a b m ()+    -> ConduitT a b m ()     -- ^ A transforming conduit-    -> ConduitM s' Void.Void m ()+    -> ConduitT s' Void.Void m ()     -- ^ A raw stream data sink. Ex: 'sinkFile outFile'     -> m () transformCSV set = transformCSV' set set@@ -475,11 +475,11 @@     -- ^ Settings to be used for input     -> CSVSettings     -- ^ Settings to be used for output-    -> ConduitM () s m ()+    -> ConduitT () s m ()     -- ^ A raw stream data source. Ex: 'sourceFile inFile'-    -> ConduitM a b m ()+    -> ConduitT a b m ()     -- ^ A transforming conduit-    -> ConduitM s' Void.Void m ()+    -> ConduitT s' Void.Void m ()     -- ^ A raw stream data sink. Ex: 'sinkFile outFile'     -> m () transformCSV' setIn setOut source c sink = runConduit $@@ -500,7 +500,7 @@  ------------------------------------------------------------------------------- -- | An efficient sink that incrementally grows a vector from the input stream-sinkVector :: (PrimMonad m, GV.Vector v a) => Int -> ConduitM a o m (v a)+sinkVector :: (PrimMonad m, GV.Vector v a) => Int -> ConduitT a o m (v a) sinkVector by = do     v <- lift $ GMV.new by     go 0 v
src/Data/CSV/Conduit/Parser/ByteString.hs view
@@ -58,15 +58,17 @@ row :: CSVSettings -> Parser (Maybe (Row ByteString)) row csvs = csvrow csvs <|> badrow +csvEndOfLine :: Parser ()+csvEndOfLine = (word8 10 >> return ()) <|> (word8 13 >> return ())  badrow :: Parser (Maybe (Row ByteString)) badrow = P.takeWhile (not . C8.isEndOfLine) *>-         (C8.endOfLine <|> C8.endOfInput) *> return Nothing+         (csvEndOfLine <|> C8.endOfInput) *> return Nothing  csvrow :: CSVSettings -> Parser (Maybe (Row ByteString)) csvrow c =   let rowbody = (quotedField' <|> field c) `sepBy` C8.char (csvSep c)-      properrow = rowbody <* (C8.endOfLine <|> P.endOfInput)+      properrow = rowbody <* (csvEndOfLine <|> P.endOfInput)       quotedField' = case csvQuoteChar c of           Nothing -> mzero           Just q' -> try (quotedField q')@@ -93,5 +95,3 @@   f <- many (C8.notChar c <|> quoted)   _ <- C8.char c   return $ B8.pack f--
src/Data/CSV/Conduit/Parser/Text.hs view
@@ -58,15 +58,17 @@ 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) *>-         (T.endOfLine <|> T.endOfInput) *> return Nothing+         (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 <* (T.endOfLine <|> P.endOfInput)+      properrow = rowbody <* (csvEndOfLine <|> P.endOfInput)       quotedField' = case csvQuoteChar c of           Nothing -> mzero           Just q' -> try (quotedField q')
test/Test.hs view
@@ -32,7 +32,9 @@ baseTests :: [Test] baseTests =   [ testCase "mapping with id works" test_identityMap,-    testCase "simple parsing works" test_simpleParse,+    testCase "simple parsing works" (test_simpleParse testFile1),+    testCase "simple parsing works for Mac-Excel" (test_simpleParse testFile3),+    testCase "fails parsing gracefully" test_parseFail,     testCase "OrderedMap" test_orderedMap   ] @@ -74,9 +76,9 @@     f :: Row Text -> [Row Text]     f = return -test_simpleParse :: IO ()-test_simpleParse = do-  (d :: V.Vector (MapRow B.ByteString)) <- readCSVFile csvSettings testFile1+test_simpleParse :: FilePath -> IO ()+test_simpleParse fp = do+  (d :: V.Vector (MapRow B.ByteString)) <- readCSVFile csvSettings fp   V.mapM_ assertRow d   where     assertRow r = v3 @=? (v1 + v2)@@ -85,6 +87,20 @@         v2 = readBS $ r Map.! "Col3"         v3 = readBS $ r Map.! "Sum" +test_parseFail :: IO ()+test_parseFail = do+  (d :: V.Vector (MapRow B.ByteString)) <- readCSVFile csvSettings testXLS+  errored <- catch (V.mapM_ assertRow d >> pure False) handler+  if errored then pure () else assertFailure "readCSVFile shouldn't read XLS"+  where+    handler :: ErrorCall -> IO Bool+    handler _ = pure True+    assertRow r = v3 @=? (v1 + v2)+      where+        v1 = readBS $ r Map.! "Col2"+        v2 = readBS $ r Map.! "Col3"+        v3 = readBS $ r Map.! "Sum"+ test_orderedMap :: IO () test_orderedMap = do   unorderedRes <-@@ -109,9 +125,13 @@ csvSettings :: CSVSettings csvSettings = defCSVSettings {csvQuoteCharAndStyle = Just ('`', DontQuoteEmpty)} -testFile1, testFile2 :: FilePath+testFile1, testFile2, testFile3 :: FilePath testFile1 = "test/test.csv" testFile2 = "test/test.csv"+testFile3 = "test/test-mac-excel.csv"++testXLS :: FilePath+testXLS = "test/test.xls"  readBS :: B.ByteString -> Int readBS = read . B.unpack
+ test/test-mac-excel.csv view
@@ -0,0 +1,1 @@+`Col1`,`Col2`,`Col3`,`Sum`
`A`,`2`,`3`,`5`
`B`,`3`,`4`,`7`
`Field using the quote char ``this is the in-quoted value```,`4`,`5`,`9`
+ test/test.xls view

binary file changed (absent → 6144 bytes)