diff -Nru haskell-csv-conduit-0.7.1.0/changelog.md haskell-csv-conduit-0.7.3.0/changelog.md --- haskell-csv-conduit-0.7.1.0/changelog.md 2019-10-11 18:35:35.000000000 +0000 +++ haskell-csv-conduit-0.7.3.0/changelog.md 2021-07-06 19:45:16.000000000 +0000 @@ -1,3 +1,10 @@ +0.7.3.0 +* Add ordered versions of named records for consistent, controllable header column ordering. [PR 44](https://github.com/ozataman/csv-conduit/pull/44) +* Add support for GHC 9.0.1 + +0.7.2.0 +* Remove some dependency upper bounds for forward compatibility. + 0.7.1.0 * Add MonadFail instance for Parser. [PR 38](https://github.com/ozataman/csv-conduit/pull/38) diff -Nru haskell-csv-conduit-0.7.1.0/csv-conduit.cabal haskell-csv-conduit-0.7.3.0/csv-conduit.cabal --- haskell-csv-conduit-0.7.1.0/csv-conduit.cabal 2019-10-11 18:35:38.000000000 +0000 +++ haskell-csv-conduit-0.7.3.0/csv-conduit.cabal 2021-07-06 19:44:46.000000000 +0000 @@ -1,5 +1,5 @@ Name: csv-conduit -Version: 0.7.1.0 +Version: 0.7.3.0 Synopsis: A flexible, fast, conduit-based CSV parser library for Haskell. Homepage: http://github.com/ozataman/csv-conduit License: BSD3 @@ -8,8 +8,8 @@ Maintainer: Ozgun Ataman Category: Data, Conduit, CSV, Text Build-type: Simple -Cabal-version: >= 1.9.2 -Tested-with: GHC == 7.6.1 +Cabal-version: >= 1.10 +Tested-with: GHC == 9.0.1, GHC == 8.10.4, GHC == 8.8.4, GHC == 8.8.3, GHC == 8.6.5, GHC == 8.4.4, GHC == 8.2.2 Description: CSV files are the de-facto standard in many situations involving data transfer, particularly when dealing with enterprise application or disparate database @@ -65,6 +65,7 @@ manual: True library + default-language: Haskell2010 exposed-modules: Data.CSV.Conduit Data.CSV.Conduit.Types @@ -82,7 +83,7 @@ attoparsec >= 0.10 , base >= 4 && < 5 , bytestring - , conduit >= 1.2.8 && < 2.0 + , conduit >= 1.2.8 , conduit-extra , containers >= 0.3 , exceptions >= 0.3 @@ -93,6 +94,7 @@ , array , blaze-builder , unordered-containers + , ordered-containers , transformers , mtl , mmorph @@ -102,10 +104,11 @@ if impl(ghc >= 7.2.1) cpp-options: -DGENERICS - build-depends: ghc-prim >= 0.2 && < 0.6 + build-depends: ghc-prim >= 0.2 test-suite test + default-language: Haskell2010 type: exitcode-stdio-1.0 main-is: Test.hs ghc-options: -Wall @@ -115,6 +118,7 @@ build-depends: base >= 4 && < 5 , bytestring + , conduit >= 1.3.0 , containers >= 0.3 , csv-conduit , directory @@ -123,6 +127,7 @@ , test-framework , test-framework-hunit , text + , ordered-containers , transformers , mtl , primitive diff -Nru haskell-csv-conduit-0.7.1.0/debian/changelog haskell-csv-conduit-0.7.3.0/debian/changelog --- haskell-csv-conduit-0.7.1.0/debian/changelog 2020-10-29 20:22:58.000000000 +0000 +++ haskell-csv-conduit-0.7.3.0/debian/changelog 2022-07-26 07:12:56.000000000 +0000 @@ -1,26 +1,9 @@ -haskell-csv-conduit (0.7.1.0-1build4) hirsute; urgency=medium +haskell-csv-conduit (0.7.3.0-1) unstable; urgency=medium - * No-change rebuild for new GHC ABIs + * Declare compliance with Debian policy 4.6.1 + * New upstream release - -- Steve Langasek Thu, 29 Oct 2020 20:22:58 +0000 - -haskell-csv-conduit (0.7.1.0-1build3) groovy; urgency=medium - - * No-change rebuild for new GHC ABIs - - -- Steve Langasek Thu, 27 Aug 2020 22:29:21 +0000 - -haskell-csv-conduit (0.7.1.0-1build2) groovy; urgency=medium - - * Rebuild against new GHC ABI. - - -- Gianfranco Costamagna Thu, 13 Aug 2020 15:33:20 +0200 - -haskell-csv-conduit (0.7.1.0-1build1) groovy; urgency=medium - - * Rebuild against new GHC abi. - - -- Gianfranco Costamagna Fri, 24 Jul 2020 11:31:22 +0200 + -- Ilias Tsitsimpis Tue, 26 Jul 2022 10:12:56 +0300 haskell-csv-conduit (0.7.1.0-1) unstable; urgency=medium diff -Nru haskell-csv-conduit-0.7.1.0/debian/control haskell-csv-conduit-0.7.3.0/debian/control --- haskell-csv-conduit-0.7.1.0/debian/control 2020-08-27 22:29:21.000000000 +0000 +++ haskell-csv-conduit-0.7.3.0/debian/control 2022-07-26 07:12:56.000000000 +0000 @@ -1,6 +1,5 @@ Source: haskell-csv-conduit -Maintainer: Ubuntu Developers -XSBC-Original-Maintainer: Debian Haskell Group +Maintainer: Debian Haskell Group Uploaders: Clint Adams Priority: optional Section: haskell @@ -16,20 +15,21 @@ libghc-blaze-builder-dev, libghc-blaze-builder-prof, libghc-conduit-dev (>= 1.2.8), - libghc-conduit-dev (<< 2.0), - libghc-conduit-prof, - libghc-conduit-extra-dev, - libghc-conduit-extra-prof, - libghc-data-default-dev, - libghc-data-default-prof, + libghc-conduit-prof (>= 0), + libghc-conduit-extra-dev (>= 0), + libghc-conduit-extra-prof (>= 0), + libghc-data-default-dev (>= 0), + libghc-data-default-prof (>= 0), libghc-exceptions-dev (>= 0.3), - libghc-exceptions-prof, - libghc-mmorph-dev, - libghc-mmorph-prof, - libghc-monad-control-dev, - libghc-monad-control-prof, - libghc-primitive-dev, - libghc-primitive-prof, + libghc-exceptions-prof (>= 0), + libghc-mmorph-dev (>= 0), + libghc-mmorph-prof (>= 0), + libghc-monad-control-dev (>= 0), + libghc-monad-control-prof (>= 0), + libghc-ordered-containers-dev (>= 0), + libghc-ordered-containers-prof (>= 0), + libghc-primitive-dev (>= 0), + libghc-primitive-prof (>= 0), libghc-resourcet-dev (>= 1.1.2.1), libghc-resourcet-prof, libghc-semigroups-dev, @@ -39,26 +39,29 @@ libghc-vector-dev, libghc-vector-prof, libghc-hunit-dev (>= 1.2), - libghc-hunit-prof, - libghc-test-framework-dev, - libghc-test-framework-prof, - libghc-test-framework-hunit-dev, - libghc-test-framework-hunit-prof, -Build-Depends-Indep: ghc-doc, - libghc-attoparsec-doc, - libghc-blaze-builder-doc, - libghc-conduit-doc, - libghc-conduit-extra-doc, - libghc-data-default-doc, - libghc-exceptions-doc, - libghc-mmorph-doc, - libghc-monad-control-doc, - libghc-primitive-doc, - libghc-resourcet-doc, - libghc-semigroups-doc, - libghc-unordered-containers-doc, - libghc-vector-doc, -Standards-Version: 4.5.0 + libghc-hunit-prof (>= 0), + libghc-conduit-dev (>= 1.3.0), + libghc-test-framework-dev (>= 0), + libghc-test-framework-prof (>= 0), + libghc-test-framework-hunit-dev (>= 0), + libghc-test-framework-hunit-prof (>= 0), +Build-Depends-Indep: + ghc-doc, + libghc-attoparsec-doc (>= 0), + libghc-blaze-builder-doc (>= 0), + libghc-conduit-doc (>= 0), + libghc-conduit-extra-doc (>= 0), + libghc-data-default-doc (>= 0), + libghc-exceptions-doc (>= 0), + libghc-mmorph-doc (>= 0), + libghc-monad-control-doc (>= 0), + libghc-ordered-containers-doc (>= 0), + libghc-primitive-doc (>= 0), + libghc-resourcet-doc (>= 0), + libghc-semigroups-doc (>= 0), + libghc-unordered-containers-doc (>= 0), + libghc-vector-doc (>= 0), +Standards-Version: 4.6.1 Homepage: https://github.com/ozataman/csv-conduit Vcs-Browser: https://salsa.debian.org/haskell-team/DHG_packages/tree/master/p/haskell-csv-conduit Vcs-Git: https://salsa.debian.org/haskell-team/DHG_packages.git [p/haskell-csv-conduit] diff -Nru haskell-csv-conduit-0.7.1.0/src/Data/CSV/Conduit/Conversion.hs haskell-csv-conduit-0.7.3.0/src/Data/CSV/Conduit/Conversion.hs --- haskell-csv-conduit-0.7.1.0/src/Data/CSV/Conduit/Conversion.hs 2019-10-11 18:33:43.000000000 +0000 +++ haskell-csv-conduit-0.7.3.0/src/Data/CSV/Conduit/Conversion.hs 2021-07-06 19:42:03.000000000 +0000 @@ -28,11 +28,15 @@ -- * Type conversion Only(..) , Named (..) + , NamedOrdered (..) , Record , NamedRecord + , NamedRecordOrdered , FromRecord(..) , FromNamedRecord(..) + , FromNamedRecordOrdered(..) , ToNamedRecord(..) + , ToNamedRecordOrdered(..) , FromField(..) , ToRecord(..) , ToField(..) @@ -47,11 +51,13 @@ , (.!) , unsafeIndex , lookup + , lookupOrdered , (.:) , namedField , (.=) , record , namedRecord + , namedRecordOrdered ) where import Control.Applicative as A @@ -61,9 +67,10 @@ import qualified Data.ByteString as B import qualified Data.ByteString.Char8 as B8 import qualified Data.ByteString.Lazy as L - import Data.Int (Int8, Int16, Int32, Int64) +import Data.Kind (Type) import qualified Data.Map as M +import qualified Data.Map.Ordered as MO import Data.Semigroup as Semigroup import qualified Data.Text as T import qualified Data.Text.Encoding as T @@ -107,6 +114,7 @@ -- | A shorthand for the ByteString case of 'MapRow' type NamedRecord = M.Map B8.ByteString B8.ByteString +type NamedRecordOrdered = MO.OMap B8.ByteString B8.ByteString -- | A wrapper around custom haskell types that can directly be @@ -116,6 +124,7 @@ -- about overlapping instances. Just use 'getNamed' to get your -- object out of the wrapper. newtype Named a = Named { getNamed :: a } deriving (Eq,Show,Read,Ord) +newtype NamedOrdered a = NamedOrdered { getNamedOrdered :: a } deriving (Eq,Show,Read,Ord) -- | A record corresponds to a single line in a CSV file. type Record = Vector B8.ByteString @@ -362,6 +371,9 @@ parseNamedRecord r = to <$> gparseNamedRecord r #endif +class FromNamedRecordOrdered a where + parseNamedRecordOrdered :: NamedRecordOrdered -> Parser a + -- | A type that can be converted to a single CSV record. -- -- An example type and instance: @@ -379,12 +391,21 @@ toNamedRecord = namedRecord . gtoRecord . from #endif +class ToNamedRecordOrdered a where + toNamedRecordOrdered :: a -> NamedRecordOrdered + instance FromField a => FromNamedRecord (M.Map B.ByteString a) where parseNamedRecord m = traverse parseField m +instance FromField a => FromNamedRecordOrdered (MO.OMap B.ByteString a) where + parseNamedRecordOrdered m = traverse parseField m + instance ToField a => ToNamedRecord (M.Map B.ByteString a) where toNamedRecord = M.map toField +instance ToField a => ToNamedRecordOrdered (MO.OMap B.ByteString a) where + toNamedRecordOrdered a = MO.fromList $ map (fmap toField) $ MO.assocs a + -- instance FromField a => FromNamedRecord (HM.HashMap B.ByteString a) where -- parseNamedRecord m = traverse (\ s -> parseField s) m @@ -700,6 +721,11 @@ where err = "no field named " ++ show (B8.unpack name) {-# INLINE lookup #-} +lookupOrdered :: FromField a => NamedRecordOrdered -> B.ByteString -> Parser a +lookupOrdered m name = maybe (fail err) parseField $ MO.lookup name m + where err = "no field named " ++ show (B8.unpack name) +{-# INLINE lookupOrdered #-} + -- | Alias for 'lookup'. (.:) :: FromField a => NamedRecord -> B.ByteString -> Parser a (.:) = lookup @@ -726,6 +752,9 @@ namedRecord :: [(B.ByteString, B.ByteString)] -> NamedRecord namedRecord = M.fromList +namedRecordOrdered :: [(B.ByteString, B.ByteString)] -> NamedRecordOrdered +namedRecordOrdered = MO.fromList + ------------------------------------------------------------------------ -- Parser for converting records to data types @@ -861,7 +890,7 @@ gparseRecordProd n = (n + 1, \v -> K1 <$> parseField (V.unsafeIndex v n)) #if MIN_VERSION_base(4,9,0) -data Proxy (s :: Meta) (f :: * -> *) a = Proxy +data Proxy (s :: Meta) (f :: Type -> Type) a = Proxy #else data Proxy s (f :: * -> *) a = Proxy #endif diff -Nru haskell-csv-conduit-0.7.1.0/src/Data/CSV/Conduit/Types.hs haskell-csv-conduit-0.7.3.0/src/Data/CSV/Conduit/Types.hs --- haskell-csv-conduit-0.7.1.0/src/Data/CSV/Conduit/Types.hs 2019-10-11 18:33:43.000000000 +0000 +++ haskell-csv-conduit-0.7.3.0/src/Data/CSV/Conduit/Types.hs 2021-07-06 17:30:08.000000000 +0000 @@ -5,7 +5,8 @@ ------------------------------------------------------------------------------- import Data.Default -import qualified Data.Map as M +import qualified Data.Map as M +import qualified Data.Map.Ordered as MO ------------------------------------------------------------------------------- @@ -51,3 +52,9 @@ -- are keys and row's individual cell values are the values of the -- 'Map'. type MapRow a = M.Map a a + +-- | An 'OrderedMapRow' is a dictionary based on 'Data.Map.Ordered' where column +-- names are keys and row's individual cell values are the values of the 'OMap'. +-- Unlike 'MapRow', 'OrderedMapRow' preserves the insertion ordering of columns. +-- 'OrderedMapRow' is a reasonable default in most cases. +type OrderedMapRow a = MO.OMap a a diff -Nru haskell-csv-conduit-0.7.1.0/src/Data/CSV/Conduit.hs haskell-csv-conduit-0.7.3.0/src/Data/CSV/Conduit.hs --- haskell-csv-conduit-0.7.1.0/src/Data/CSV/Conduit.hs 2019-10-11 18:39:26.000000000 +0000 +++ haskell-csv-conduit-0.7.3.0/src/Data/CSV/Conduit.hs 2021-07-06 17:30:08.000000000 +0000 @@ -20,12 +20,14 @@ , transformCSV' , mapCSVFile , writeHeaders + , writeHeadersOrdered -- Types , CSV (..) , CSVSettings (..) , defCSVSettings , MapRow + , OrderedMapRow , Row -- * Re-exported For Convenience @@ -52,6 +54,7 @@ sourceFile) import qualified Data.Conduit.List as C import qualified Data.Map as M +import qualified Data.Map.Ordered as MO import Data.String import Data.Text (Text) import qualified Data.Text as T @@ -63,8 +66,11 @@ import System.IO ------------------------------------------------------------------------------- import Data.CSV.Conduit.Conversion (FromNamedRecord (..), + FromNamedRecordOrdered (..), Named (..), + NamedOrdered (..), ToNamedRecord (..), + ToNamedRecordOrdered (..), runParser) import qualified Data.CSV.Conduit.Parser.ByteString as BSP import qualified Data.CSV.Conduit.Parser.Text as TP @@ -224,6 +230,11 @@ intoCSV set = intoCSVMap set fromCSV set = fromCSVMap set +instance (CSV s (Row s'), Ord s', IsString s) => CSV s (OrderedMapRow s') where + rowToStr s r = rowToStr s . (map snd . MO.assocs) $ r + intoCSV set = intoCSVMapOrdered set + fromCSV set = fromCSVMapOrdered set + ------------------------------------------------------------------------------- intoCSVMap :: (Ord a, MonadThrow m, CSV s [a]) @@ -239,6 +250,19 @@ converter hs = awaitForever $ yield . toMapCSV hs toMapCSV !hs !fs = M.fromList $ zip hs fs +intoCSVMapOrdered :: (Ord a, MonadThrow m, CSV s [a]) + => CSVSettings -> ConduitM s (OrderedMapRow a) m () +intoCSVMapOrdered set = intoCSV set .| (headers >>= converter) + where + headers = do + mrow <- await + case mrow of + Nothing -> return [] + Just [] -> headers + Just hs -> return hs + converter hs = awaitForever $ yield . toMapCSV hs + toMapCSV !hs !fs = MO.fromList $ zip hs fs + -- | Conversion of stream directly to/from a custom complex haskell -- type. @@ -254,6 +278,18 @@ where go = toNamedRecord . getNamed +instance (FromNamedRecordOrdered a, ToNamedRecordOrdered a, CSV s (OrderedMapRow ByteString)) => + CSV s (NamedOrdered a) where + rowToStr s a = rowToStr s . toNamedRecordOrdered . getNamedOrdered $ a + intoCSV set = intoCSV set .| C.mapMaybe go + where + go x = either (const Nothing) (Just . NamedOrdered) $ + runParser (parseNamedRecordOrdered x) + + fromCSV set = C.map go .| fromCSV set + where + go = toNamedRecordOrdered . getNamedOrdered + ------------------------------------------------------------------------------- fromCSVMap :: (Monad m, IsString s, CSV s [a]) @@ -262,6 +298,12 @@ where push r = mapM_ yield [rowToStr set (M.elems r), "\n"] +fromCSVMapOrdered :: (Monad m, IsString s, CSV s [a]) + => CSVSettings -> ConduitM (MO.OMap k a) s m () +fromCSVMapOrdered set = awaitForever push + where + push r = mapM_ yield [rowToStr set (map snd $ MO.assocs r), "\n"] + ------------------------------------------------------------------------------- -- | Write headers AND the row into the output stream, once. If you @@ -284,6 +326,19 @@ , rowToStr set (M.elems row) , "\n" ] +writeHeadersOrdered + :: (Monad m, CSV s (Row r), IsString s) + => CSVSettings + -> ConduitM (OrderedMapRow r) s m () +writeHeadersOrdered set = do + mrow <- await + case mrow of + Nothing -> return () + Just row -> mapM_ yield [ rowToStr set (map fst $ MO.assocs row) + , "\n" + , rowToStr set (map snd $ MO.assocs row) + , "\n" ] + --------------------------- -- Convenience Functions -- diff -Nru haskell-csv-conduit-0.7.1.0/test/Test.hs haskell-csv-conduit-0.7.3.0/test/Test.hs --- haskell-csv-conduit-0.7.1.0/test/Test.hs 2019-10-11 18:33:43.000000000 +0000 +++ haskell-csv-conduit-0.7.3.0/test/Test.hs 2021-07-06 17:47:43.000000000 +0000 @@ -1,103 +1,116 @@ {-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} module Main where -import Control.Exception -import qualified Data.ByteString.Char8 as B -import Data.Map ((!)) -import Data.Monoid as M -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 (assertFailure, (@=?)) - -import Data.CSV.Conduit -import Data.CSV.Conduit.Conversion - +import qualified Conduit as C +import Control.Exception +import qualified Data.ByteString.Char8 as B +import Data.CSV.Conduit +import Data.CSV.Conduit.Conversion +import qualified Data.Map as Map +import qualified Data.Map.Ordered as OMap +import Data.Monoid as M +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 (assertFailure, (@=?), (@?=)) main :: IO () main = defaultMain tests - tests :: [Test] tests = - [ testGroup "Basic Ops" baseTests - , testGroup "decodeCSV" decodeCSVTests + [ testGroup "Basic Ops" baseTests, + testGroup "decodeCSV" decodeCSVTests ] - baseTests :: [Test] baseTests = - [ testCase "mapping with id works" test_identityMap - , testCase "simple parsing works" test_simpleParse + [ testCase "mapping with id works" test_identityMap, + testCase "simple parsing works" test_simpleParse, + testCase "OrderedMap" test_orderedMap ] - 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 + 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) + Left e -> assertFailure (show e) Right foos -> M.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) - + _ -> fail ("Expected \"foo\" but got " <> B.unpack s) instance ToNamedRecord Foo where toNamedRecord Foo = namedRecord ["Foo" .= ("foo" :: B.ByteString)] - test_identityMap :: IO () test_identityMap = do - _ <- runResourceT $ mapCSVFile csvSettings f testFile2 outFile - f1 <- readFile testFile2 - f2 <- readFile outFile - f1 @=? f2 - removeFile outFile + _ <- runResourceT $ mapCSVFile csvSettings f testFile2 outFile + f1 <- readFile testFile2 + f2 <- readFile outFile + f1 @=? f2 + removeFile outFile where outFile = "test/testOut.csv" f :: Row Text -> [Row Text] f = return - test_simpleParse :: IO () test_simpleParse = do (d :: V.Vector (MapRow B.ByteString)) <- readCSVFile csvSettings testFile1 V.mapM_ assertRow d where assertRow r = v3 @=? (v1 + v2) - where v1 = readBS $ r ! "Col2" - v2 = readBS $ r ! "Col3" - v3 = readBS $ r ! "Sum" - + where + v1 = readBS $ r Map.! "Col2" + v2 = readBS $ r Map.! "Col3" + v3 = readBS $ r Map.! "Sum" + +test_orderedMap :: IO () +test_orderedMap = do + unorderedRes <- + C.runConduit $ + C.yieldMany [unorderedRow] + C..| writeHeaders defCSVSettings + C..| C.foldC + unorderedRes @?= ("\"a\",\"b\"\n\"aval\",\"bval\"\n" :: B.ByteString) + orderedRes <- + C.runConduit $ + C.yieldMany [orderedRow] + C..| writeHeadersOrdered defCSVSettings + C..| C.foldC + orderedRes @?= ("\"b\",\"a\"\n\"bval\",\"aval\"\n" :: B.ByteString) + where + orderedRow :: OrderedMapRow Text + orderedRow = OMap.fromList pairs + unorderedRow :: MapRow Text + unorderedRow = Map.fromList pairs + pairs = [("b", "bval"), ("a", "aval")] csvSettings :: CSVSettings -csvSettings = defCSVSettings { csvQuoteChar = Just '`'} +csvSettings = defCSVSettings {csvQuoteChar = Just '`'} testFile1, testFile2 :: FilePath testFile1 = "test/test.csv" testFile2 = "test/test.csv" - readBS :: B.ByteString -> Int readBS = read . B.unpack