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 +38/−36
- changelog.md +5/−0
- csv-conduit.cabal +14/−29
- src/Data/CSV/Conduit.hs +82/−61
- src/Data/CSV/Conduit/Conversion.hs +9/−10
- src/Data/CSV/Conduit/Conversion/Internal.hs +3/−1
- test/Bench.hs +0/−21
- test/Test.hs +41/−4
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 ()