packages feed

sv 1.0 → 1.1

raw patch · 4 files changed

+142/−7 lines, 4 filesdep ~sv-corePVP ok

version bump matches the API change (PVP)

Dependency ranges changed: sv-core

API changes (from Hackage documentation)

+ Data.Sv: parseDecodeNamed :: NameDecode' ByteString a -> ParseOptions -> ByteString -> DecodeValidation ByteString [a]
+ Data.Sv: parseDecodeNamedFromDsvCursor :: NameDecode' ByteString a -> ParseOptions -> DsvCursor -> DecodeValidation ByteString [a]
+ Data.Sv: parseDecodeNamedFromFile :: MonadIO m => NameDecode' ByteString a -> ParseOptions -> FilePath -> m (DecodeValidation ByteString [a])

Files

changelog.md view
@@ -1,6 +1,12 @@ # Revision history for sv +## 1.1 -- 2018-07-25++* Add column-name-based decoding by adding parseDecodeNamed and associated functions+* Support sv-core 0.2+ ## 1.0 -- 2018-07-19+ * Split the package into sv, sv-core, svfactor. This removes Data.Sv.{Parse,Print,Syntax}   and changes types in Data.Sv.Decode and Data.Sv.Encode.   Compatibility with version 0.1 is minimal
src/Data/Sv.hs view
@@ -1,3 +1,6 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TupleSections #-}+ {-| Module      : Data.Sv Copyright   : (C) CSIRO 2017-2018@@ -22,6 +25,9 @@     parseDecode   , parseDecodeFromFile   , parseDecodeFromDsvCursor+  , parseDecodeNamed+  , parseDecodeNamedFromFile+  , parseDecodeNamedFromDsvCursor   , decode   , decodeMay   , decodeEither@@ -87,6 +93,17 @@       cursor = DSV.makeCursor sep bs   in  parseDecodeFromDsvCursor d opts cursor +-- | Parse a 'ByteString' as an Sv, and then decode it with the given colum+-- based decoder.+parseDecodeNamed ::+  NameDecode' ByteString a+  -> ParseOptions+  -> LBS.ByteString+  -> DecodeValidation ByteString [a]+parseDecodeNamed d opts bs =+  parseDecodeNamedFromDsvCursor d opts+    (DSV.makeCursor (_separator opts) bs)+ -- | Load a file, parse it, and decode it. parseDecodeFromFile ::   MonadIO m@@ -97,11 +114,41 @@ parseDecodeFromFile d opts fp =   parseDecode d opts <$> liftIO (LBS.readFile fp) +-- | Load a file, parse it, and decode it by column.+parseDecodeNamedFromFile ::+  MonadIO m+  => NameDecode' ByteString a+  -> ParseOptions+  -> FilePath+  -> m (DecodeValidation ByteString [a])+parseDecodeNamedFromFile d opts fp =+  parseDecodeNamed d opts <$> liftIO (LBS.readFile fp)+ -- | Decode from a 'DsvCursor' parseDecodeFromDsvCursor :: Decode' ByteString a -> ParseOptions -> DsvCursor -> DecodeValidation ByteString [a] parseDecodeFromDsvCursor d opts cursor =-  let toField = Prelude.either badParse pure . parse-      parse = first UTF8.fromString . AL.eitherResult . AL.parse field-  in  flip bindValidation (decode d) . traverse (traverse toField) . DSV.toListVector $ case _headedness opts of-        Unheaded -> cursor-        Headed   -> nextPosition (nextRow cursor)+  flip bindValidation (decode d) . traverse (traverse toField) . DSV.toListVector $ case _headedness opts of+    Unheaded -> cursor+    Headed   -> nextPosition (nextRow cursor)++-- | Decode from a 'DsvCursor'+parseDecodeNamedFromDsvCursor :: NameDecode' ByteString a -> ParseOptions -> DsvCursor -> DecodeValidation ByteString [a]+parseDecodeNamedFromDsvCursor n opts cursor =+  let parts =+        case DSV.toListVector cursor of+          [] -> missingHeader+          (header:body) ->+            let header' = traverse toField header+            in  case _headedness opts of+              Unheaded ->+                badConfig $ mconcat+                  [ "Your ParseOptions indicates a CSV with no header (Unheaded),\n"+                  , "but your decoder requires column names."+                  ]+              Headed   ->+                (,body) <$> bindValidation header' (flip makePositional n)+  in  bindValidation parts $ \(d,b) ->+      flip bindValidation (decode d) $ traverse (traverse toField) b++toField :: LBS.ByteString -> DecodeValidation ByteString ByteString+toField = Prelude.either badParse pure . first UTF8.fromString . AL.eitherResult . AL.parse field
sv.cabal view
@@ -1,5 +1,5 @@ name:                sv-version:             1.0+version:             1.1 license:             BSD3 license-file:        LICENCE author:              George Wilson@@ -44,6 +44,7 @@   Examples:   .   * Decoding a real CSV: <https://github.com/qfpl/sv/blob/master/examples/src/Data/Sv/Example/Species.lhs Species.lhs>+  * Decoding by column name: <https://github.com/qfpl/sv/blob/master/examples/src/Data/Sv/Example/Columnar.hs Columnar.hs>   * Encoding data to a CSV: <https://github.com/qfpl/sv/blob/master/examples/src/Data/Sv/Example/Encoding.hs Encoding.hs>   * Handling NULL and Unknown occuring in a column of numbers: <https://github.com/qfpl/sv/blob/master/examples/src/Data/Sv/Example/Numbers.hs Numbers.hs>   * Dealing with non-rectangular data: <https://github.com/qfpl/sv/blob/master/examples/src/Data/Sv/Example/Ragged.hs Ragged.hs>@@ -96,7 +97,7 @@                        , contravariant >= 1.2 && < 1.6                        , hw-dsv >= 0.2.1 && < 0.3                        , semigroupoids >= 5 && <6-                       , sv-core >= 0.1 && < 0.2+                       , sv-core >= 0.2 && < 0.3                        , transformers >= 0.2 && < 0.6                        , utf8-string >= 1 && < 1.1                        , validation >= 1 && < 1.1
test/Data/Sv/DecodeTest.hs view
@@ -28,6 +28,7 @@     intOrStringTest   , varyingLengthTest   , semigroupoidTest+  , namedTest   ]  data IntOrString =@@ -120,4 +121,84 @@   , testCase "Does the right thing in the case of right failure" $       parseDecode semiD opts semiTestString3 @?=         Failure (DecodeErrors (pure (BadDecode "Couldn't parse \"false\" as a double")))+  ]++-- This CSV has enough columns to make an Item, it has more columns than a+-- SemiItem/SemiItem2, and not enough columns for a SuperItem/SuperItem2+namedCsv :: LBS.ByteString+namedCsv =+  LBS.intercalate "\n" [+    "id,name,cost,units"+  , "1,fridge,600,5"+  , "2,stereo,1499.99,2"+  , "3,dryer,748.95,3"+  ]++data Item = Item Int Text Double Int deriving (Eq, Show)+data SemiItem = SemiItem Text Double deriving (Eq, Show)+data SemiItem2 = SemiItem2 Int Double deriving (Eq, Show)+data SuperItem = SuperItem Int Text Text Double Int deriving (Eq, Show)+data SuperItem2 = SuperItem2 Int Text Text Double Int Double deriving (Eq, Show)++inOrder :: NameDecode' ByteString Item+inOrder =+  Item <$> D.column "id" D.int <*> D.column "name" D.utf8+    <*> D.column "cost" D.double <*> D.column "units" D.int++outOrder :: NameDecode' ByteString Item+outOrder =+  (\n u c i -> Item i n c u) <$> D.column "name" D.utf8+    <*> D.column "units" D.int <*> D.column "cost" D.double+    <*> D.column "id" D.int++inOrderSemi :: NameDecode' ByteString SemiItem+inOrderSemi =+  SemiItem <$> D.column "name" D.utf8 <*> D.column "cost" D.double++outOrderSemi :: NameDecode' ByteString SemiItem2+outOrderSemi =+  SemiItem2 <$> D.column "units" D.int <*> D.column "cost" D.double++super :: NameDecode' ByteString SuperItem+super =+  SuperItem <$> D.column "id" D.int <*> D.column "name" D.utf8+    <*> D.column "manufacturer" D.utf8+    <*> D.column "cost" D.double <*> D.column "units" D.int++super2 :: NameDecode' ByteString SuperItem2+super2 =+  SuperItem2 <$> D.column "id" D.int <*> D.column "name" D.utf8+    <*> D.column "manufacturer" D.utf8+    <*> D.column "cost" D.double <*> D.column "units" D.int+    <*> D.column "profit" D.double++namedTest :: TestTree+namedTest = testGroup "Named decodes"+  [ testCase "columns in order" $+      parseDecodeNamed inOrder defaultParseOptions namedCsv @?=+        pure [Item 1 "fridge" 600 5, Item 2 "stereo" 1499.99 2, Item 3 "dryer" 748.95 3]+  , testCase "columns out of order" $+      parseDecodeNamed outOrder defaultParseOptions namedCsv @?=+        pure [Item 1 "fridge" 600 5, Item 2 "stereo" 1499.99 2, Item 3 "dryer" 748.95 3]+  , testCase "columns in order, with some skipped" $+      parseDecodeNamed inOrderSemi defaultParseOptions namedCsv @?=+        pure [SemiItem "fridge" 600, SemiItem "stereo" 1499.99, SemiItem "dryer" 748.95]+  , testCase "columns out of order, with some skipped" $+      parseDecodeNamed outOrderSemi defaultParseOptions namedCsv @?=+        pure [SemiItem2 5 600, SemiItem2 2 1499.99, SemiItem2 3 748.95]+  , testCase "one missing column" $+      parseDecodeNamed super defaultParseOptions namedCsv @?=+        Failure (DecodeErrors (MissingColumn "manufacturer":|[]))+  , testCase "multiple missing columns" $+      parseDecodeNamed super2 defaultParseOptions namedCsv @?=+        Failure (DecodeErrors (MissingColumn "manufacturer":|[MissingColumn "profit"]))+  , testCase "empty document (missing header)" $+      parseDecodeNamed inOrder defaultParseOptions "" @?=+        Failure (DecodeErrors (MissingHeader:|[]))+  , testCase "misconfigured" $+      parseDecodeNamed inOrder opts namedCsv @?=+        Failure (DecodeErrors (+          BadConfig "Your ParseOptions indicates a CSV with no header (Unheaded),\nbut your decoder requires column names."+          :|[]+        ))   ]