cassava 0.4.0.0 → 0.4.1.0
raw patch · 7 files changed
+73/−30 lines, 7 filesdep ~attoparsecPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: attoparsec
API changes (from Hackage documentation)
Files
- Data/Csv/Conversion.hs +22/−17
- Data/Csv/Encoding.hs +2/−2
- Data/Csv/Incremental.hs +2/−1
- Data/Csv/Parser.hs +2/−7
- Data/Csv/Util.hs +23/−1
- cassava.cabal +2/−2
- tests/UnitTests.hs +20/−0
Data/Csv/Conversion.hs view
@@ -32,7 +32,7 @@ ) where import Control.Applicative (Alternative, Applicative, (<*>), (<$>), (<|>),- empty, pure)+ (<*), (*>), empty, pure) import Control.Monad (MonadPlus, mplus, mzero) import Data.Attoparsec.Char8 (double) import qualified Data.Attoparsec.Char8 as A8@@ -429,7 +429,7 @@ toField = toField . T.encodeUtf8 . T.singleton {-# INLINE toField #-} --- | Accepts same syntax as 'rational'.+-- | Accepts same syntax as 'rational'. Ignores whitespace. instance FromField Double where parseField = parseDouble {-# INLINE parseField #-}@@ -440,7 +440,7 @@ toField = realFloat {-# INLINE toField #-} --- | Accepts same syntax as 'rational'.+-- | Accepts same syntax as 'rational'. Ignores whitespace. instance FromField Float where parseField s = double2Float <$> parseDouble s {-# INLINE parseField #-}@@ -452,12 +452,12 @@ {-# INLINE toField #-} parseDouble :: B.ByteString -> Parser Double-parseDouble s = case parseOnly double s of+parseDouble s = case parseOnly (ws *> double <* ws) s of Left err -> typeError "Double" s (Just err) Right n -> pure n {-# INLINE parseDouble #-} --- | Accepts a signed decimal number.+-- | Accepts a signed decimal number. Ignores whitespace. instance FromField Int where parseField = parseSigned "Int" {-# INLINE parseField #-}@@ -467,7 +467,7 @@ toField = decimal {-# INLINE toField #-} --- | Accepts a signed decimal number.+-- | Accepts a signed decimal number. Ignores whitespace. instance FromField Integer where parseField = parseSigned "Integer" {-# INLINE parseField #-}@@ -477,7 +477,7 @@ toField = decimal {-# INLINE toField #-} --- | Accepts a signed decimal number.+-- | Accepts a signed decimal number. Ignores whitespace. instance FromField Int8 where parseField = parseSigned "Int8" {-# INLINE parseField #-}@@ -487,7 +487,7 @@ toField = decimal {-# INLINE toField #-} --- | Accepts a signed decimal number.+-- | Accepts a signed decimal number. Ignores whitespace. instance FromField Int16 where parseField = parseSigned "Int16" {-# INLINE parseField #-}@@ -497,7 +497,7 @@ toField = decimal {-# INLINE toField #-} --- | Accepts a signed decimal number.+-- | Accepts a signed decimal number. Ignores whitespace. instance FromField Int32 where parseField = parseSigned "Int32" {-# INLINE parseField #-}@@ -507,7 +507,7 @@ toField = decimal {-# INLINE toField #-} --- | Accepts a signed decimal number.+-- | Accepts a signed decimal number. Ignores whitespace. instance FromField Int64 where parseField = parseSigned "Int64" {-# INLINE parseField #-}@@ -517,7 +517,7 @@ toField = decimal {-# INLINE toField #-} --- | Accepts an unsigned decimal number.+-- | Accepts an unsigned decimal number. Ignores whitespace. instance FromField Word where parseField = parseUnsigned "Word" {-# INLINE parseField #-}@@ -527,7 +527,7 @@ toField = decimal {-# INLINE toField #-} --- | Accepts an unsigned decimal number.+-- | Accepts an unsigned decimal number. Ignores whitespace. instance FromField Word8 where parseField = parseUnsigned "Word8" {-# INLINE parseField #-}@@ -537,7 +537,7 @@ toField = decimal {-# INLINE toField #-} --- | Accepts an unsigned decimal number.+-- | Accepts an unsigned decimal number. Ignores whitespace. instance FromField Word16 where parseField = parseUnsigned "Word16" {-# INLINE parseField #-}@@ -547,7 +547,7 @@ toField = decimal {-# INLINE toField #-} --- | Accepts an unsigned decimal number.+-- | Accepts an unsigned decimal number. Ignores whitespace. instance FromField Word32 where parseField = parseUnsigned "Word32" {-# INLINE parseField #-}@@ -557,7 +557,7 @@ toField = decimal {-# INLINE toField #-} --- | Accepts an unsigned decimal number.+-- | Accepts an unsigned decimal number. Ignores whitespace. instance FromField Word64 where parseField = parseUnsigned "Word64" {-# INLINE parseField #-}@@ -614,16 +614,21 @@ {-# INLINE toField #-} parseSigned :: (Integral a, Num a) => String -> B.ByteString -> Parser a-parseSigned typ s = case parseOnly (A8.signed A8.decimal) s of+parseSigned typ s = case parseOnly (ws *> A8.signed A8.decimal <* ws) s of Left err -> typeError typ s (Just err) Right n -> pure n {-# INLINE parseSigned #-} parseUnsigned :: Integral a => String -> B.ByteString -> Parser a-parseUnsigned typ s = case parseOnly A8.decimal s of+parseUnsigned typ s = case parseOnly (ws *> A8.decimal <* ws) s of Left err -> typeError typ s (Just err) Right n -> pure n {-# INLINE parseUnsigned #-}++ws :: A8.Parser ()+ws = A8.skipWhile (\c -> c == ' ' || c == '\t')++ ------------------------------------------------------------------------ -- Custom version of attoparsec @parseOnly@ function which fails if
Data/Csv/Encoding.hs view
@@ -33,7 +33,7 @@ toLazyByteString, toByteString) import Blaze.ByteString.Builder.Char8 (fromString) import Control.Applicative ((*>), (<|>), optional, pure)-import Data.Attoparsec.Char8 (endOfInput, endOfLine)+import Data.Attoparsec.Char8 (endOfInput) import qualified Data.Attoparsec.ByteString.Lazy as AL import qualified Data.ByteString as B import qualified Data.ByteString.Char8 as B8@@ -54,7 +54,7 @@ import qualified Data.Csv.Parser as Parser import Data.Csv.Types hiding (toNamedRecord) import qualified Data.Csv.Types as Types-import Data.Csv.Util (blankLine)+import Data.Csv.Util (blankLine, endOfLine) -- TODO: 'encode' isn't as efficient as it could be.
Data/Csv/Incremental.hs view
@@ -29,7 +29,7 @@ import Control.Applicative ((<*), (<|>)) import qualified Data.Attoparsec as A-import Data.Attoparsec.Char8 (endOfInput, endOfLine)+import Data.Attoparsec.Char8 (endOfInput) import qualified Data.ByteString as B import qualified Data.Vector as V @@ -37,6 +37,7 @@ import qualified Data.Csv.Conversion as Conversion import Data.Csv.Parser import Data.Csv.Types+import Data.Csv.Util (endOfLine) -- $feed-header --
Data/Csv/Parser.hs view
@@ -27,7 +27,7 @@ import Blaze.ByteString.Builder (fromByteString, toByteString) import Blaze.ByteString.Builder.Char.Utf8 (fromChar) import Control.Applicative ((*>), (<$>), (<*), optional, pure)-import Data.Attoparsec.Char8 (char, endOfInput, endOfLine)+import Data.Attoparsec.Char8 (char, endOfInput) import qualified Data.Attoparsec as A import qualified Data.Attoparsec.Lazy as AL import qualified Data.Attoparsec.Zepto as Z@@ -38,7 +38,7 @@ import Data.Word (Word8) import Data.Csv.Types-import Data.Csv.Util ((<$!>), blankLine, liftM2')+import Data.Csv.Util ((<$!>), blankLine, endOfLine, liftM2', cr, newline, doubleQuote) -- | Options that controls how data is decoded. These options can be -- used to e.g. decode tab-separated data instead of comma-separated@@ -186,8 +186,3 @@ if done then return (acc `mappend` fromByteString h) else rest--doubleQuote, newline, cr :: Word8-doubleQuote = 34-newline = 10-cr = 13
Data/Csv/Util.hs view
@@ -1,13 +1,22 @@-{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE BangPatterns, OverloadedStrings #-} module Data.Csv.Util ( (<$!>) , blankLine , liftM2'+ , endOfLine+ , doubleQuote+ , newline+ , cr ) where +import Control.Applicative ((<|>), (*>))+import Data.Word (Word8)+import Data.Attoparsec.ByteString.Char8 (string)+import qualified Data.Attoparsec as A import qualified Data.ByteString as B import qualified Data.Vector as V+import Data.Attoparsec (Parser) -- | A strict version of 'Data.Functor.<$>' for monads. (<$!>) :: Monad m => (a -> b) -> m a -> m b@@ -30,3 +39,16 @@ y <- b return (f x y) {-# INLINE liftM2' #-}+++-- | Match either a single newline character @\'\\n\'@, or a carriage+-- return followed by a newline character @\"\\r\\n\"@, or a single+-- carriage return @\'\\r\'@.+endOfLine :: Parser ()+endOfLine = (A.word8 newline *> return ()) <|> (string "\r\n" *> return ()) <|> (A.word8 cr *> return ())+{-# INLINE endOfLine #-}++doubleQuote, newline, cr :: Word8+doubleQuote = 34+newline = 10+cr = 13
cassava.cabal view
@@ -1,5 +1,5 @@ Name: cassava-Version: 0.4.0.0+Version: 0.4.1.0 Synopsis: A CSV parsing and encoding library Description: A CSV parsing and encoding library optimized for ease of use and high@@ -36,7 +36,7 @@ Build-depends: array < 0.6,- attoparsec >= 0.10.2 && < 0.12,+ attoparsec >= 0.10.2 && < 0.13, base < 5, blaze-builder < 0.4, bytestring < 0.11,
tests/UnitTests.hs view
@@ -192,6 +192,9 @@ [[("field1", "abc"), ("field2", "def")]]) , ("twoRecords", "field\r\nabc\r\ndef\r\n", ["field"], [[("field", "abc")], [("field", "def")]])+ , ("cr header", "field\rabc", ["field"], [[("field", "abc")]])+ , ("cr trailing", "field\rabc\r", ["field"], [[("field", "abc")]])+ , ("cr separator", "field\rabc\rdef", ["field"], [[("field", "abc")],[("field","def")]]) ] encodeTest (name, hdr, input, expected) =@@ -241,6 +244,12 @@ Left _ -> return () Right _ -> assertFailure "expected partial field decode" +expect :: (Eq a, Show a) => Parser a -> a -> Assertion+expect p a0 =+ case runParser p of+ Right a -> a @=? a0+ Left e -> assertFailure e+ conversionTests :: [TF.Test] conversionTests = [ testGroup "roundTrip"@@ -290,6 +299,17 @@ (parseField "1.0+" :: Parser Double)) , testCase "Integer" (partialDecode (parseField "1e6" :: Parser Integer))+ ]+ , testGroup "Space trimming"+ [ testCase "_Int" (expect (parseField " 12" :: Parser Int) 12)+ , testCase "Int_" (expect (parseField "12 " :: Parser Int) 12)+ , testCase "_Int_" (expect (parseField " 12 " :: Parser Int) 12)+ , testCase "_Word" (expect (parseField " 12" :: Parser Word) 12)+ , testCase "Word_" (expect (parseField "12 " :: Parser Word) 12)+ , testCase "_Word_" (expect (parseField " 12 " :: Parser Word) 12)+ , testCase "_Double" (expect (parseField " 1.2e1" :: Parser Double) 12)+ , testCase "Double_" (expect (parseField "1.2e1 " :: Parser Double) 12)+ , testCase "_Double_" (expect (parseField " 1.2e1 " :: Parser Double) 12) ] ]