packages feed

csv-conduit 0.6.7 → 0.6.8

raw patch · 8 files changed

+192/−162 lines, 8 filesdep +exceptionsdep ~conduitPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: exceptions

Dependency ranges changed: conduit

API changes (from Hackage documentation)

- Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.FromField a => Data.CSV.Conduit.Conversion.FromNamedRecord (Data.Map.Base.Map Data.ByteString.Internal.ByteString a)
- Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.ToField a => Data.CSV.Conduit.Conversion.ToNamedRecord (Data.Map.Base.Map Data.ByteString.Internal.ByteString a)
+ Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.FromField a => Data.CSV.Conduit.Conversion.FromNamedRecord (Data.Map.Internal.Map Data.ByteString.Internal.ByteString a)
+ Data.CSV.Conduit.Conversion: instance Data.CSV.Conduit.Conversion.ToField a => Data.CSV.Conduit.Conversion.ToNamedRecord (Data.Map.Internal.Map Data.ByteString.Internal.ByteString a)
- Data.CSV.Conduit: decodeCSV :: (Vector v a, CSV s a) => CSVSettings -> s -> Either SomeException (v a)
+ Data.CSV.Conduit: decodeCSV :: forall v a s. (Vector v a, CSV s a) => CSVSettings -> s -> Either SomeException (v a)
- Data.CSV.Conduit: fromCSV :: (CSV s r, Monad m) => CSVSettings -> Conduit r m s
+ Data.CSV.Conduit: fromCSV :: (CSV s r, Monad m) => CSVSettings -> ConduitM r s m ()
- Data.CSV.Conduit: intoCSV :: (CSV s r, MonadThrow m) => CSVSettings -> Conduit s m r
+ Data.CSV.Conduit: intoCSV :: (CSV s r, (MonadThrow m)) => CSVSettings -> ConduitM s r m ()
- Data.CSV.Conduit: mapCSVFile :: (MonadResource m, MonadThrow m, CSV ByteString a, CSV ByteString b) => CSVSettings -> (a -> [b]) -> FilePath -> FilePath -> m ()
+ Data.CSV.Conduit: mapCSVFile :: (MonadResource m, CSV ByteString a, CSV ByteString b, MonadThrow m) => CSVSettings -> (a -> [b]) -> FilePath -> FilePath -> m ()
- Data.CSV.Conduit: runResourceT :: MonadBaseControl IO m => ResourceT m a -> m a
+ Data.CSV.Conduit: runResourceT :: MonadUnliftIO m => ResourceT m a -> m a
- Data.CSV.Conduit: transformCSV :: (MonadThrow m, CSV s a, CSV s' b) => CSVSettings -> Source m s -> Conduit a m b -> Sink s' m () -> 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 -> CSVSettings -> Source m s -> Conduit a m b -> Sink s' 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: writeHeaders :: (Monad m, CSV s (Row r), IsString s) => CSVSettings -> Conduit (MapRow r) m s
+ Data.CSV.Conduit: writeHeaders :: (Monad m, CSV s (Row r), IsString s) => CSVSettings -> ConduitM (MapRow r) s m ()
- Data.CSV.Conduit.Conversion: class FromNamedRecord a where parseNamedRecord r = to <$> gparseNamedRecord r
+ Data.CSV.Conduit.Conversion: class FromNamedRecord a
- Data.CSV.Conduit.Conversion: class FromRecord a where parseRecord r = to <$> gparseRecord r
+ Data.CSV.Conduit.Conversion: class FromRecord a
- Data.CSV.Conduit.Conversion: class ToNamedRecord a where toNamedRecord = namedRecord . gtoRecord . from
+ Data.CSV.Conduit.Conversion: class ToNamedRecord a
- Data.CSV.Conduit.Conversion: class ToRecord a where toRecord = fromList . gtoRecord . from
+ Data.CSV.Conduit.Conversion: class ToRecord a

Files

README.md view
@@ -58,8 +58,8 @@ * CSVeable is parameterized on both a stream type and a target CSV row type. * There are 2 basic row types and they implement *exactly* the same operations,   so you can chose the right one for the job at hand:-  - type MapRow t = Map t t-  - type Row t = [t]+  - `type MapRow t = Map t t`+  - `type Row t = [t]` * You basically use the Conduits defined in this library to do the   parsing from a CSV stream and rendering back into a CSV stream. * Use the full flexibility and modularity of conduits for sources and sinks.@@ -76,46 +76,48 @@  #### Example #1: Basics Using Convenience API -    {-# LANGUAGE OverloadedStrings #-}+```haskell+{-# LANGUAGE OverloadedStrings #-} -    import Data.Conduit-    import Data.Conduit.Binary-    import Data.Conduit.List as CL-    import Data.CSV.Conduit-    import Data.Text (Text)-    -    -- Just reverse te columns-    myProcessor :: Monad m => Conduit (Row Text) m (Row Text)-    myProcessor = CL.map reverse-    -    test :: IO ()-    test = runResourceT $ -      transformCSV defCSVSettings -                   (sourceFile "input.csv") -                   myProcessor-                   (sinkFile "output.csv")+import Data.Conduit+import Data.Conduit.Binary+import Data.Conduit.List as CL+import Data.CSV.Conduit+import Data.Text (Text) +-- Just reverse te columns+myProcessor :: Monad m => Conduit (Row Text) m (Row Text)+myProcessor = CL.map reverse +test :: IO ()+test = runResourceT $ +  transformCSV defCSVSettings +               (sourceFile "input.csv") +               myProcessor+               (sinkFile "output.csv")+```+ #### Example #2: Basics Using Conduit API -    {-# LANGUAGE OverloadedStrings #-}+```haskell+{-# LANGUAGE OverloadedStrings #-} -    import Data.Conduit-    import Data.Conduit.Binary-    import Data.CSV.Conduit-    import Data.Text (Text)+import Data.Conduit+import Data.Conduit.Binary+import Data.CSV.Conduit+import Data.Text (Text) -    myProcessor :: Monad m => Conduit (Row Text) m (Row Text)-    myProcessor = awaitForever $ yield-    -    -- Let's simply stream from a file, parse the CSV, reserialize it-    -- and push back into another file.-    test :: IO ()-    test = runResourceT $ -      sourceFile "test/BigFile.csv" $= -      intoCSV defCSVSettings $=-      myProcessor $=-      fromCSV defCSVSettings $$-      sinkFile "test/BigFileOut.csv"+myProcessor :: Monad m => Conduit (Row Text) m (Row Text)+myProcessor = awaitForever $ yield +-- Let's simply stream from a file, parse the CSV, reserialize it+-- and push back into another file.+test :: IO ()+test = runResourceT $ +  sourceFile "test/BigFile.csv" $= +  intoCSV defCSVSettings $=+  myProcessor $=+  fromCSV defCSVSettings $$+  sinkFile "test/BigFileOut.csv"+``` 
changelog.md view
@@ -1,2 +1,7 @@+0.6.8+* Haddocks improvements+* Fix inlining and specialization rules around formatDecimal+* Updates to permit newest conduit/resourcet packages+ 0.6.7 * Fix build for GHC 8.0.1
csv-conduit.cabal view
@@ -1,5 +1,5 @@ Name:                csv-conduit-Version:             0.6.7+Version:             0.6.8 Synopsis:            A flexible, fast, conduit-based CSV parser library for Haskell. Homepage:            http://github.com/ozataman/csv-conduit License:             BSD3@@ -59,8 +59,11 @@   changelog.md   test/test.csv   test/Test.hs-  test/Bench.hs +flag lib-Werror+  default: False+  manual: True+ library   exposed-modules:       Data.CSV.Conduit@@ -72,14 +75,17 @@       Data.CSV.Conduit.Conversion.Internal       Data.CSV.Conduit.Monoid   ghc-options: -Wall -funbox-strict-fields+  if flag(lib-Werror)+    ghc-options: -Werror   hs-source-dirs: src   build-depends:       attoparsec             >= 0.10     , base                   >= 4 && < 5     , bytestring-    , conduit                >= 1.0 && < 2.0+    , conduit                >= 1.2.8 && < 2.0     , conduit-extra     , containers             >= 0.3+    , exceptions             >= 0.3     , monad-control     , text     , data-default@@ -92,7 +98,6 @@     , mmorph     , primitive     , resourcet              >= 1.1.2.1-  ghc-prof-options: -fprof-auto    if impl(ghc >= 7.2.1)     cpp-options: -DGENERICS@@ -103,6 +108,8 @@   type: exitcode-stdio-1.0   main-is: Test.hs   ghc-options: -Wall+  if flag(lib-Werror)+    ghc-options: -Werror   hs-source-dirs: test   build-depends:       base >= 4 && < 5@@ -119,29 +126,7 @@     , mtl     , primitive -flag bench-  default: False-  manual: True -executable bench-  main-is: Bench.hs-  if flag(bench)-    buildable: True-  else-    buildable: False-  ghc-options: -Wall-  hs-source-dirs: test-  build-depends:-      base >= 4 && < 5-    , bytestring-    , containers >= 0.3-    , csv-conduit-    , vector-    , directory-    , text-    , transformers-    , mtl-    , primitive--  ghc-options: -rtsopts-  ghc-prof-options: -rtsopts -caf-all -auto-all+source-repository head+  type:     git+  location: git://github.com/ozataman/csv-conduit.git
src/Data/CSV/Conduit.hs view
@@ -1,9 +1,11 @@ {-# LANGUAGE BangPatterns          #-}+{-# LANGUAGE CPP                   #-} {-# LANGUAGE FlexibleContexts      #-} {-# LANGUAGE FlexibleInstances     #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings     #-} {-# LANGUAGE RankNTypes            #-}+{-# LANGUAGE ScopedTypeVariables   #-} {-# LANGUAGE TypeSynonymInstances  #-} {-# LANGUAGE UndecidableInstances  #-} @@ -32,12 +34,12 @@  ------------------------------------------------------------------------------- import           Control.Exception-import           Control.Monad.Morph+import           Control.Monad.Catch.Pure           (CatchT)+import           Control.Monad.Catch.Pure           (runCatchT)+import           Control.Monad.Except import           Control.Monad.Primitive import           Control.Monad.ST-import           Control.Monad.Trans import           Control.Monad.Trans.Resource       (MonadResource, MonadThrow,-                                                     runExceptionT,                                                      runResourceT) import           Data.Attoparsec.Types              (Parser) import qualified Data.ByteString                    as B@@ -57,6 +59,7 @@ import qualified Data.Vector                        as V import qualified Data.Vector.Generic                as GV import qualified Data.Vector.Generic.Mutable        as GMV+import           Data.Void                          (Void) import           System.IO ------------------------------------------------------------------------------- import           Data.CSV.Conduit.Conversion        (FromNamedRecord (..),@@ -111,11 +114,11 @@ -- >myProcessor :: Conduit (MapRow Text) m (MapRow Text) -- >myProcessor = undefined -- >--- >test = runResourceT $--- >  sourceFile "test/BigFile.csv" $=--- >  intoCSV defCSVSettings $=--- >  myProcessor $=--- >  (writeHeaders defCSVSettings >> fromCSV defCSVSettings) $$+-- >test = runResourceT $ runConduit $+-- >  sourceFile "test/BigFile.csv" .|+-- >  intoCSV defCSVSettings .|+-- >  myProcessor .|+-- >  (writeHeaders defCSVSettings >> fromCSV defCSVSettings) .| -- >  sinkFile "test/BigFileOut.csv" class CSV s r where @@ -126,13 +129,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 -> Conduit s m r+  intoCSV :: (MonadThrow m) => CSVSettings -> ConduitM 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 -> Conduit r m s+  fromCSV :: Monad m => CSVSettings -> ConduitM r s m ()   @@ -145,8 +148,8 @@     let       sep = B.pack [c2w (csvSep s)]       wrapField !f = case csvQuoteChar s of-        Just !x -> (x `B8.cons` escape x f) `B8.snoc` x-        _ -> f+        Just !x-> (x `B8.cons` escape x f) `B8.snoc` x+        _      -> f       escape c str = B8.intercalate (B8.pack [c,c]) $ B8.split c str     in B.intercalate sep . map wrapField $ r @@ -161,8 +164,8 @@     let       sep = T.pack [csvSep s]       wrapField !f = case csvQuoteChar s of-        Just !x -> x `T.cons` escape x f `T.snoc` x-        _ -> f+        Just !x-> x `T.cons` escape x f `T.snoc` x+        _      -> f       escape c str = T.intercalate (T.pack [c,c]) $ T.split (== c) str     in T.intercalate sep . map wrapField $ r @@ -174,8 +177,8 @@ -- | 'Row' instance using 'Text' based on 'ByteString' stream instance CSV ByteString (Row Text) where     rowToStr s r = T.encodeUtf8 $ rowToStr s r-    intoCSV set = intoCSV set =$= C.map (map T.decodeUtf8)-    fromCSV set = fromCSV set =$= C.map T.encodeUtf8+    intoCSV set = intoCSV set .| C.map (map T.decodeUtf8)+    fromCSV set = fromCSV set .| C.map T.encodeUtf8   @@ -185,28 +188,28 @@ -- lots of unnecessary overhead. Included for convenience. instance CSV ByteString (Row String) where     rowToStr s r = rowToStr s $ map B8.pack r-    intoCSV set = intoCSV set =$= C.map (map B8.unpack)-    fromCSV set = C.map (map B8.pack) =$= fromCSV set+    intoCSV set = intoCSV set .| C.map (map B8.unpack)+    fromCSV set = C.map (map B8.pack) .| fromCSV set   -- | Support for parsing rows in the 'Vector' form. instance (CSV s (Row s)) => CSV s (V.Vector s) where     rowToStr s r = rowToStr s . V.toList $ r-    intoCSV set = intoCSV set =$= C.map (V.fromList)-    fromCSV set = C.map (V.toList) =$= fromCSV set+    intoCSV set = intoCSV set .| C.map (V.fromList)+    fromCSV set = C.map (V.toList) .| fromCSV set    ------------------------------------------------------------------------------- fromCSVRow :: (Monad m, IsString s, CSV s r)-           => CSVSettings -> Conduit r m s+           => CSVSettings -> ConduitM r s m () fromCSVRow set = awaitForever $ \row -> mapM_ yield [rowToStr set row, "\n"]    --------------------------------------------------------------------------------intoCSVRow :: (MonadThrow m, AttoparsecInput i) => Parser i (Maybe o) -> Conduit i m o-intoCSVRow p = parse =$= puller+intoCSVRow :: (MonadThrow m, AttoparsecInput i) => Parser i (Maybe o) -> ConduitM i o m ()+intoCSVRow p = parse .| puller   where     parse = {-# SCC "conduitParser_p" #-} conduitParser p     puller = {-# SCC "puller" #-}@@ -224,8 +227,8 @@  ------------------------------------------------------------------------------- intoCSVMap :: (Ord a, MonadThrow m, CSV s [a])-           => CSVSettings -> Conduit s m (MapRow a)-intoCSVMap set = intoCSV set =$= (headers >>= converter)+           => CSVSettings -> ConduitM s (MapRow a) m ()+intoCSVMap set = intoCSV set .| (headers >>= converter)   where     headers = do       mrow <- await@@ -242,19 +245,19 @@ instance (FromNamedRecord a, ToNamedRecord a, CSV s (MapRow ByteString)) =>     CSV s (Named a) where     rowToStr s a = rowToStr s . toNamedRecord . getNamed $ a-    intoCSV set = intoCSV set =$= C.mapMaybe go+    intoCSV set = intoCSV set .| C.mapMaybe go         where           go x = either (const Nothing) (Just . Named) $                  runParser (parseNamedRecord x) -    fromCSV set = C.map go =$= fromCSV set+    fromCSV set = C.map go .| fromCSV set         where           go = toNamedRecord . getNamed   ------------------------------------------------------------------------------- fromCSVMap :: (Monad m, IsString s, CSV s [a])-           => CSVSettings -> Conduit (M.Map k a) m s+           => CSVSettings -> ConduitM (M.Map k a) s m () fromCSVMap set = awaitForever push   where     push r = mapM_ yield [rowToStr set (M.elems r), "\n"]@@ -267,11 +270,11 @@ -- -- Usage: Just chain this using the 'Monad' instance in your pipeline: ----- > ... =$= writeHeaders settings >> fromCSV settings $$ sinkFile "..."+-- > runConduit $ ... .| writeHeaders settings >> fromCSV settings .| sinkFile "..." writeHeaders     :: (Monad m, CSV s (Row r), IsString s)     => CSVSettings-    -> Conduit (MapRow r) m s+    -> ConduitM (MapRow r) s m () writeHeaders set = do   mrow <- await   case mrow of@@ -294,8 +297,9 @@     => CSVSettings -- ^ Settings to use in deciphering stream     -> FilePath    -- ^ Input file     -> m (V.Vector a)-readCSVFile set fp = liftIO . runResourceT $ sourceFile fp $= intoCSV set $$ hoist lift (sinkVector 10)-+readCSVFile set fp = liftIO . runResourceT $ runConduit $ sourceFile fp .| intoCSV set .| transPipe lift (sinkVector growthFactor)+  where+    growthFactor = 10   -------------------------------------------------------------------------------@@ -306,15 +310,27 @@ -- For example for 'ByteString': -- -- >>> s <- LB.readFile "my.csv"--- >>> decodeCSV 'def' s :: Vector (Vector ByteString)+-- >>> decodeCSV defCSVSettings s :: Either SomeException (Vector (Vector ByteString)) ----- will just work.+-- will work as long as the data is comma separated. decodeCSV-    :: (GV.Vector v a, CSV s a)+    :: forall v a s. (GV.Vector v a, CSV s a)     => CSVSettings     -> s     -> Either SomeException (v a)-decodeCSV set bs = runST $ runExceptionT $ C.sourceList [bs] $= intoCSV set $$ hoist lift (sinkVector 10)+decodeCSV set bs = runST $ runExceptT pipeline+  where+    src :: ConduitM () s (ExceptT SomeException (ST s1)) ()+    src = C.sourceList [bs]+    csvConvert :: ConduitM s a (ExceptT SomeException (ST s1)) ()+    csvConvert = transPipe (ExceptT . runCatchT) csvConvert'+    csvConvert' :: ConduitM s a (CatchT (ST s1)) ()+    csvConvert' = intoCSV set+    growthFactor = 10+    sink :: ConduitM a Void (ExceptT SomeException (ST s1)) (v a)+    sink = sinkVector growthFactor+    pipeline :: ExceptT SomeException (ST s1) (v a)+    pipeline = runConduit (src .| csvConvert .| sink)   @@ -332,8 +348,8 @@   -> [a]   -- ^ List of rows   -> IO ()-writeCSVFile set fo fmode rows = runResourceT $ do-  C.sourceList rows $= fromCSV set $$+writeCSVFile set fo fmode rows = runResourceT $ runConduit $ do+  C.sourceList rows .| fromCSV set .|     sinkIOHandle (openFile fo fmode)  @@ -344,16 +360,22 @@ -- An easy way to run this function would be 'runResourceT' after -- feeding it all the arguments. mapCSVFile-    :: (MonadResource m, MonadThrow m, CSV ByteString a, CSV ByteString b)-    => CSVSettings-    -- ^ Settings to use both for both input and output-    -> (a -> [b])-    -- ^ A mapping function-    -> FilePath-    -- ^ Input file-    -> FilePath-    -- ^ Output file-    -> m ()+    :: ( MonadResource m+       , CSV ByteString a+       , CSV ByteString b+# if MIN_VERSION_resourcet(1,2,0)+       , MonadThrow m+#endif+       )+      => CSVSettings+      -- ^ Settings to use both for both input and output+      -> (a -> [b])+      -- ^ A mapping function+      -> FilePath+      -- ^ Input file+      -> FilePath+      -- ^ Output file+      -> m () mapCSVFile set f fi fo =   transformCSV set (sourceFile fi) (C.concatMap f) (sinkFile fo) @@ -365,11 +387,11 @@     :: (MonadThrow m, CSV s a, CSV s' b)     => CSVSettings     -- ^ Settings to be used for both input and output-    -> Source m s+    -> ConduitM () s m ()     -- ^ A raw stream data source. Ex: 'sourceFile inFile'-    -> Conduit a m b+    -> ConduitM a b m ()     -- ^ A transforming conduit-    -> Sink s' m ()+    -> ConduitM s' Void m ()     -- ^ A raw stream data sink. Ex: 'sinkFile outFile'     -> m () transformCSV set = transformCSV' set set@@ -393,18 +415,18 @@     -- ^ Settings to be used for input     -> CSVSettings     -- ^ Settings to be used for output-    -> Source m s+    -> ConduitM () s m ()     -- ^ A raw stream data source. Ex: 'sourceFile inFile'-    -> Conduit a m b+    -> ConduitM a b m ()     -- ^ A transforming conduit-    -> Sink s' m ()+    -> ConduitM s' Void m ()     -- ^ A raw stream data sink. Ex: 'sinkFile outFile'     -> m ()-transformCSV' setIn setOut source c sink =-    source $=-    intoCSV setIn $=-    c $=-    fromCSV setOut $$+transformCSV' setIn setOut source c sink = runConduit $+    source .|+    intoCSV setIn .|+    c .|+    fromCSV setOut .|     sink  @@ -433,8 +455,7 @@             return $! v'           Just x -> do             v' <- case GMV.length v == i of-                    True -> lift $ GMV.grow v by+                    True  -> lift $ GMV.grow v by                     False -> return v             lift $ GMV.write v' i x             go (i+1) v'-
src/Data/CSV/Conduit/Conversion.hs view
@@ -54,8 +54,7 @@     , namedRecord     ) where -import Control.Applicative (Alternative, Applicative, (<*>), (<$>), (<|>),-                                       empty, pure)+import Control.Applicative as A import Control.Monad (MonadPlus, mplus, mzero) import Data.Attoparsec.ByteString.Char8 (double, parseOnly) import qualified Data.Attoparsec.ByteString.Char8 as A8@@ -65,16 +64,16 @@  import Data.Int (Int8, Int16, Int32, Int64) import qualified Data.Map as M-import Data.Monoid (Monoid, mappend, mempty)+import Data.Monoid as Monoid import qualified Data.Text as T import qualified Data.Text.Encoding as T import qualified Data.Text.Lazy as LT import qualified Data.Text.Lazy.Encoding as LT-import Data.Traversable (traverse)+import Data.Traversable as DT import Data.Vector (Vector, (!)) import qualified Data.Vector as V import qualified Data.Vector.Unboxed as U-import Data.Word (Word, Word8, Word16, Word32, Word64)+import Data.Word as W import GHC.Float (double2Float) import Prelude hiding (lookup, takeWhile) @@ -155,7 +154,7 @@    #ifdef GENERICS     default parseRecord :: (Generic a, GFromRecord (Rep a)) => Record -> Parser a-    parseRecord r = to <$> gparseRecord r+    parseRecord r = to A.<$> gparseRecord r #endif  -- | Haskell lacks a single-element tuple type, so if you CSV data@@ -309,7 +308,7 @@             | otherwise     = show expected ++ "-tuple"  instance FromField a => FromRecord [a] where-    parseRecord = traverse parseField . V.toList+    parseRecord = DT.traverse parseField . V.toList  instance ToField a => ToRecord [a] where     toRecord = V.fromList . map toField@@ -553,7 +552,7 @@     {-# INLINE toField #-}  -- | Accepts an unsigned decimal number.-instance FromField Word where+instance FromField W.Word where     parseField = parseUnsigned "Word"     {-# INLINE parseField #-} @@ -648,7 +647,7 @@     toField = toField . T.pack     {-# INLINE toField #-} -parseSigned :: (Integral a, Num a) => String -> B.ByteString -> Parser a+parseSigned :: (Integral a) => String -> B.ByteString -> Parser a parseSigned typ s = case parseOnly (A8.signed A8.decimal) s of     Left err -> typeError typ s (Just err)     Right n  -> pure n@@ -779,7 +778,7 @@                                    in unParser a kf' ks     {-# INLINE mplus #-} -instance Monoid (Parser a) where+instance Monoid.Monoid (Parser a) where     mempty  = fail "mempty"     {-# INLINE mempty #-}     mappend = mplus
src/Data/CSV/Conduit/Conversion/Internal.hs view
@@ -24,9 +24,10 @@ -- TODO: Add an optimized version for Integer.  formatDecimal :: Integral a => a -> Builder-{-# SPECIALIZE formatDecimal :: Int8 -> Builder #-} {-# RULES "formatDecimal/Int" formatDecimal = formatBoundedSigned     :: Int -> Builder #-}+{-# RULES "formatDecimal/Int8" formatDecimal = formatBoundedSigned+    :: Int8 -> Builder #-} {-# RULES "formatDecimal/Int16" formatDecimal = formatBoundedSigned     :: Int16 -> Builder #-} {-# RULES "formatDecimal/Int32" formatDecimal = formatBoundedSigned@@ -43,6 +44,7 @@     :: Word32 -> Builder #-} {-# RULES "formatDecimal/Word64" formatDecimal = formatPositive     :: Word64 -> Builder #-}+{-# NOINLINE formatDecimal #-} formatDecimal i     | i < 0     = minus <>                   if i <= -128
− test/Bench.hs
@@ -1,21 +0,0 @@--module Main where--import qualified Data.ByteString.Char8 as B-import Data.Map ((!))-import Data.Text-import System.Directory-import System.Environment-import Data.CSV.Conduit---main = do-    inPath:_ <- getArgs-    runResourceT $ mapCSVFile defCSVSettings idF inPath outPath-    removeFile outPath-  where-    outPath = "test/testOut.csv"---idF :: Row Text -> [Row Text]-idF = return . id
test/Test.hs view
@@ -1,18 +1,22 @@-{-# LANGUAGE OverloadedStrings   #-}-{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings     #-}+{-# LANGUAGE ScopedTypeVariables   #-}  module Main where +import           Control.Exception import qualified Data.ByteString.Char8          as B import           Data.Map                       ((!))+import           Data.Monoid import           Data.Text import qualified Data.Vector                    as V import           System.Directory import           Test.Framework                 (Test, defaultMain, testGroup) import           Test.Framework.Providers.HUnit-import           Test.HUnit                     ((@=?))+import           Test.HUnit                     (assertFailure, (@=?))  import           Data.CSV.Conduit+import           Data.CSV.Conduit.Conversion   main :: IO ()@@ -20,7 +24,10 @@   tests :: [Test]-tests = [testGroup "Basic Ops" baseTests]+tests =+  [ testGroup "Basic Ops" baseTests+  , testGroup "decodeCSV" decodeCSVTests+  ]   baseTests :: [Test]@@ -28,6 +35,36 @@   [ testCase "mapping with id works" test_identityMap   , testCase "simple parsing works" test_simpleParse   ]+++decodeCSVTests :: [Test]+decodeCSVTests =+  [ testCase "parses a CSV" $ do+      let efoos = decodeCSV defCSVSettings ("Foo\nfoo" :: B.ByteString)+      case efoos :: Either SomeException (V.Vector (Named Foo)) of+        Left e     -> assertFailure (show e)+        Right foos -> V.fromList [Named Foo] @=? foos+  , testCase "eats parse errors, evidently" $ do+      let efoos = decodeCSV defCSVSettings ("Foo\nbad" :: B.ByteString)+      case efoos :: Either SomeException (V.Vector (Named Foo)) of+        Left e     -> assertFailure (show e)+        Right foos -> mempty @=? foos+  ]+++data Foo = Foo deriving (Show, Eq)+++instance FromNamedRecord Foo where+  parseNamedRecord nr = do+    s <- nr .: "Foo"+    case s of+      "foo" -> pure Foo+      _ -> fail ("Expected \"foo\" but got " <> B.unpack s)+++instance ToNamedRecord Foo where+  toNamedRecord Foo = namedRecord ["Foo" .= ("foo" :: B.ByteString)]   test_identityMap :: IO ()