pipes-key-value-csv 0.1.0.0 → 0.2.0.0
raw patch · 24 files changed
+1053/−1054 lines, 24 filesdep ~validationsetup-changed
Dependency ranges changed: validation
Files
- LICENSE +30/−30
- README.md +2/−2
- Setup.hs +2/−2
- pipes-key-value-csv.cabal +1/−2
- src/Pipes/KeyValueCsv.hs +73/−73
- src/Pipes/KeyValueCsv/Cell.hs +57/−57
- src/Pipes/KeyValueCsv/Common.hs +105/−105
- src/Pipes/KeyValueCsv/Csv.hs +50/−50
- src/Pipes/KeyValueCsv/Internal.hs +82/−82
- src/Pipes/KeyValueCsv/Internal/Csv.hs +100/−100
- src/Pipes/KeyValueCsv/Internal/KeyValue.hs +6/−6
- src/Pipes/KeyValueCsv/Internal/Names.hs +30/−30
- src/Pipes/KeyValueCsv/Internal/Types.hs +36/−36
- src/Pipes/KeyValueCsv/KeyValue.hs +9/−9
- src/Pipes/KeyValueCsv/Names.hs +35/−35
- src/Pipes/KeyValueCsv/Types.hs +54/−54
- src/Pipes/KeyValueCsv/Types/Common.hs +40/−40
- src/Pipes/KeyValueCsv/Types/Csv.hs +52/−52
- src/Pipes/KeyValueCsv/Types/KeyValue.hs +59/−59
- test/Test/Common.hs +46/−46
- test/Test/Csv.hs +58/−58
- test/Test/KeyValue.hs +67/−67
- test/Test/Types.hs +42/−42
- test/test-kvc.hs +17/−17
LICENSE view
@@ -1,30 +1,30 @@-Copyright (c) 2015, Marcin Mrotek - -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - - * Neither the name of Marcin Mrotek nor the names of other - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +Copyright (c) 2015, Marcin Mrotek++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Marcin Mrotek nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
README.md view
@@ -1,2 +1,2 @@-# key-value-csv -Processing CSV files preceded by key-value pairs. +# key-value-csv+Processing CSV files preceded by key-value pairs.
Setup.hs view
@@ -1,2 +1,2 @@-import Distribution.Simple -main = defaultMain +import Distribution.Simple+main = defaultMain
pipes-key-value-csv.cabal view
@@ -1,5 +1,5 @@ name: pipes-key-value-csv -version: 0.1.0.0 +version: 0.2.0.0 synopsis: Streaming processing of CSV files preceded by key-value pairs. description: Nonstandard CSV files, with the table preceded by a header consisting of key-value pairs are often the output format of varius test equipment, such as digital oscilloscopes. homepage: https://github.com/marcinmrotek/key-value-csv @@ -48,7 +48,6 @@ , pipes-text >= 0.0 && < 0.1 , reflection >= 1.5 && < 2.2 , text >= 1.2 && < 1.3 - , validation >= 0.5.2 && < 0.6 , vinyl >= 0.5.2 && < 0.6 , vinyl-utils >= 0.2.0.1 && < 0.4 hs-source-dirs: src
src/Pipes/KeyValueCsv.hs view
@@ -1,73 +1,73 @@-{-| -Module : Pipes.KeyValueCsv -Copyright : (c) Marcin Mrotek, 2015 -License : BSD3 -Maintainer : marcin.jan.mrotek@gmail.com -Stability : experimental - -Parse CSV files with key-value headers. --} - -{-# LANGUAGE - DataKinds - , ExplicitForAll - , PolyKinds - , TypeOperators - #-} - -module Pipes.KeyValueCsv - ( breakLines - , parseKeyValueCsv - , module Pipes.KeyValueCsv.Csv - , module Pipes.KeyValueCsv.KeyValue - , module Pipes.KeyValueCsv.Types - , Record (..) - -- * Re-exports - , AccValidation(..) - ) where - -import Prelude hiding (lines) - -import Pipes.KeyValueCsv.Internal -import Pipes.KeyValueCsv.Internal.Types -import Pipes.KeyValueCsv.Common -import Pipes.KeyValueCsv.Csv -import Pipes.KeyValueCsv.KeyValue -import Pipes.KeyValueCsv.Types - -import Control.Lens -import Data.Validation -import Data.Vinyl -import Data.Vinyl.Functor -import Data.Vinyl.Utils.Proxy - -breakLines - :: Monad m - => (Text -> Bool) - -> Lines m r - -> Lines m (Lines m r) -{-^ -Break a stream of lines into two parts, on a line that satisfies the given predicate. -All input lines up to the breaking one will be fully read, and each (not including the breaking one) will be re-'yield'ed. --} -breakLines p (Lines l) = Lines $ Lines <$> breakLines' p l - -parseKeyValueCsv - :: forall (m :: * -> *) (f :: k -> *) (g :: j -> *) (hs :: [k]) (rs :: [j]) (r :: *) - . ( Monad m - , Record hs - ) - => Options m f g hs rs - -> Producer Text m r - -> m - ( Rec (WithKeyValueError :. f) hs - , Producer (Rec (WithCsvError :. g) rs) m r - ) --- ^Read a CSV file preceded by key-value pairs. -parseKeyValueCsv options producer = useDelimiter (options^.delimiter) $ do - (hdr, remaining) - <- parseKeyValues (options^.kvOptions) - . breakLines (options^.predicate) - $ lines producer - pure (hdr, parseCsv (options^.csvOptions) remaining) - +{-|+Module : Pipes.KeyValueCsv+Copyright : (c) Marcin Mrotek, 2015+License : BSD3+Maintainer : marcin.jan.mrotek@gmail.com+Stability : experimental++Parse CSV files with key-value headers.+-}++{-# LANGUAGE + DataKinds+ , ExplicitForAll+ , PolyKinds+ , TypeOperators + #-}++module Pipes.KeyValueCsv + ( breakLines+ , parseKeyValueCsv+ , module Pipes.KeyValueCsv.Csv+ , module Pipes.KeyValueCsv.KeyValue+ , module Pipes.KeyValueCsv.Types+ , Record (..)+ -- * Re-exports+ , Validation(..)+ ) where++import Prelude hiding (lines)++import Pipes.KeyValueCsv.Internal+import Pipes.KeyValueCsv.Internal.Types+import Pipes.KeyValueCsv.Common+import Pipes.KeyValueCsv.Csv+import Pipes.KeyValueCsv.KeyValue+import Pipes.KeyValueCsv.Types++import Control.Lens+import Data.Validation+import Data.Vinyl+import Data.Vinyl.Functor+import Data.Vinyl.Utils.Proxy++breakLines + :: Monad m+ => (Text -> Bool)+ -> Lines m r+ -> Lines m (Lines m r)+{-^ +Break a stream of lines into two parts, on a line that satisfies the given predicate.+All input lines up to the breaking one will be fully read, and each (not including the breaking one) will be re-'yield'ed.+-}+breakLines p (Lines l) = Lines $ Lines <$> breakLines' p l++parseKeyValueCsv + :: forall (m :: * -> *) (f :: k -> *) (g :: j -> *) (hs :: [k]) (rs :: [j]) (r :: *)+ . ( Monad m+ , Record hs+ )+ => Options m f g hs rs+ -> Producer Text m r+ -> m + ( Rec (WithKeyValueError :. f) hs+ , Producer (Rec (WithCsvError :. g) rs) m r+ )+-- ^Read a CSV file preceded by key-value pairs.+parseKeyValueCsv options producer = useDelimiter (options^.delimiter) $ do+ (hdr, remaining)+ <- parseKeyValues (options^.kvOptions) + . breakLines (options^.predicate) + $ lines producer+ pure (hdr, parseCsv (options^.csvOptions) remaining)+
src/Pipes/KeyValueCsv/Cell.hs view
@@ -1,57 +1,57 @@-{-| -Module : Pipes.KeyValueCsv.Cell -Copyright : (c) Marcin Mrotek, 2015 -License : BSD3 -Maintainer : marcin.jan.mrotek@gmail.com -Stability : experimental - -Automated parsing of single cells. --} - -{-# LANGUAGE - FlexibleContexts - , FlexibleInstances - , GeneralizedNewtypeDeriving - , PolyKinds - , RankNTypes - , TypeSynonymInstances - #-} - -module Pipes.KeyValueCsv.Cell where - -import Pipes.KeyValueCsv.Internal - -import Data.Text (Text) -import qualified Data.Text.Lazy as Lazy -import Data.Text.Lazy.Read -import Data.Vinyl.Functor -import Pipes.Parse (Parser) -import Pipes.Parse.Tutorial() - --- |Parse a single cell. For more information about parsing with Pipes, see "Pipes.Parse.Tutorial". -class FromCell c where - fromCell :: Monad m => Parser Text m (Either String c) - -instance FromCell Text where - fromCell = Right . Lazy.toStrict <$> drawText - -instance FromCell String where - fromCell = Right . Lazy.unpack <$> drawText - -instance FromCell Integer where - fromCell = fmap fst . signed decimal <$> (skipSpace >> build) - -instance FromCell Int where - fromCell = fmap fst . signed decimal <$> (skipSpace >> build) - -instance FromCell Double where - fromCell = fmap fst . double <$> (skipSpace >> build) - -instance FromCell a => FromCell (Identity a) where - fromCell = fmap Identity <$> fromCell - -instance FromCell (f (g a)) => FromCell (Compose f g a) where - fromCell = fmap Compose <$> fromCell - -instance FromCell a => FromCell (Thunk a) where - fromCell = fmap Thunk <$> fromCell +{-|+Module : Pipes.KeyValueCsv.Cell+Copyright : (c) Marcin Mrotek, 2015+License : BSD3+Maintainer : marcin.jan.mrotek@gmail.com+Stability : experimental++Automated parsing of single cells.+-}++{-# LANGUAGE + FlexibleContexts+ , FlexibleInstances+ , GeneralizedNewtypeDeriving+ , PolyKinds+ , RankNTypes + , TypeSynonymInstances+ #-}++module Pipes.KeyValueCsv.Cell where++import Pipes.KeyValueCsv.Internal++import Data.Text (Text)+import qualified Data.Text.Lazy as Lazy+import Data.Text.Lazy.Read+import Data.Vinyl.Functor+import Pipes.Parse (Parser)+import Pipes.Parse.Tutorial()++-- |Parse a single cell. For more information about parsing with Pipes, see "Pipes.Parse.Tutorial".+class FromCell c where+ fromCell :: Monad m => Parser Text m (Either String c)++instance FromCell Text where+ fromCell = Right . Lazy.toStrict <$> drawText++instance FromCell String where+ fromCell = Right . Lazy.unpack <$> drawText++instance FromCell Integer where+ fromCell = fmap fst . signed decimal <$> (skipSpace >> build)++instance FromCell Int where+ fromCell = fmap fst . signed decimal <$> (skipSpace >> build)++instance FromCell Double where+ fromCell = fmap fst . double <$> (skipSpace >> build)++instance FromCell a => FromCell (Identity a) where+ fromCell = fmap Identity <$> fromCell++instance FromCell (f (g a)) => FromCell (Compose f g a) where+ fromCell = fmap Compose <$> fromCell++instance FromCell a => FromCell (Thunk a) where+ fromCell = fmap Thunk <$> fromCell
src/Pipes/KeyValueCsv/Common.hs view
@@ -1,105 +1,105 @@-{-| -Module : Pipes.KeyValueCsv.Common -Copyright : (c) Marcin Mrotek, 2015 -License : BSD3 -Maintainer : marcin.jan.mrotek@gmail.com -Stability : experimental - -Utility functions. --} - -{-# LANGUAGE - DataKinds - , GADTs - , FlexibleContexts - , PolyKinds - , RankNTypes - , ScopedTypeVariables - , TypeOperators - #-} - -module Pipes.KeyValueCsv.Common - ( module Pipes.KeyValueCsv.Common - , Lines - , Cells - , Producer - , Text - ) where - -import Pipes.KeyValueCsv.Cell -import Pipes.KeyValueCsv.Internal.Types -import Pipes.KeyValueCsv.Types.Common - -import Control.Lens -import Data.Reflection -import Data.Vinyl -import Data.Vinyl.Functor -import Data.Vinyl.TypeLevel -import Data.Vinyl.Utils.Proxy -import Data.Text (Text) -import Pipes hiding (Proxy) -import qualified Pipes.Group as Group -import Pipes.Parse (Parser) -import qualified Pipes.Text as PipesText - -lines - :: Monad m - => Producer Text m r - -> Lines m r --- ^Split a 'Text' stream into lines. -lines = - Lines - . Group.maps Line - . view PipesText.lines - -cells - :: ( Monad m - , Given Delimiter - ) - => Line m r - -> Cells m r --- ^Split a line into delimited cells. -cells = - Cells - . Group.maps Cell - . view (PipesText.splits delim) - . unLine - where delim = getDelimiter given - -useDelimiter :: Char -> (Given Delimiter => a) -> a -{-^ -Provide a delimiter for a computation. -Do not use this function if there is already a 'Given' 'Delimiter' in scope. --} -useDelimiter = give . Delimiter - -wrapParser - :: forall m f r . Functor m - => Parser Text m (Either String (f r)) - -> CellParser m f r --- ^Wrap a parser to use it as a type constructor for 'Rec'. -wrapParser p = Compose wrapped - where - wrapped :: WrapParser Text m ((Either String :. f) r) - wrapped = WrapParser composed - composed :: Parser Text m ((Either String :. f) r) - composed = Compose <$> p - -defaultParser' - :: ( RecAll f rs FromCell - , Monad m - ) - => Rec Proxy rs - -> Rec (CellParser m f) rs -defaultParser' RNil = RNil -defaultParser' (Proxy :& ps) = wrapParser fromCell :& defaultParser' ps - -defaultParser - :: forall (m :: * -> *) (f :: k -> *) (rs :: [k]) - . ( RecAll f rs FromCell - , Monad m - , Record rs - ) - => Rec (CellParser m f) rs -defaultParser = defaultParser' proxyRecord - +{-|+Module : Pipes.KeyValueCsv.Common+Copyright : (c) Marcin Mrotek, 2015+License : BSD3+Maintainer : marcin.jan.mrotek@gmail.com+Stability : experimental++Utility functions.+-}++{-# LANGUAGE + DataKinds+ , GADTs+ , FlexibleContexts+ , PolyKinds+ , RankNTypes+ , ScopedTypeVariables+ , TypeOperators+ #-}++module Pipes.KeyValueCsv.Common + ( module Pipes.KeyValueCsv.Common+ , Lines+ , Cells+ , Producer+ , Text+ ) where++import Pipes.KeyValueCsv.Cell+import Pipes.KeyValueCsv.Internal.Types+import Pipes.KeyValueCsv.Types.Common++import Control.Lens+import Data.Reflection+import Data.Vinyl+import Data.Vinyl.Functor+import Data.Vinyl.TypeLevel+import Data.Vinyl.Utils.Proxy+import Data.Text (Text)+import Pipes hiding (Proxy)+import qualified Pipes.Group as Group+import Pipes.Parse (Parser)+import qualified Pipes.Text as PipesText++lines + :: Monad m + => Producer Text m r + -> Lines m r+-- ^Split a 'Text' stream into lines.+lines = + Lines + . Group.maps Line+ . view PipesText.lines++cells + :: ( Monad m+ , Given Delimiter+ )+ => Line m r + -> Cells m r+-- ^Split a line into delimited cells.+cells =+ Cells+ . Group.maps Cell+ . view (PipesText.splits delim)+ . unLine+ where delim = getDelimiter given++useDelimiter :: Char -> (Given Delimiter => a) -> a+{-^+Provide a delimiter for a computation. +Do not use this function if there is already a 'Given' 'Delimiter' in scope.+-}+useDelimiter = give . Delimiter++wrapParser + :: forall m f r . Functor m+ => Parser Text m (Either String (f r)) + -> CellParser m f r+-- ^Wrap a parser to use it as a type constructor for 'Rec'.+wrapParser p = Compose wrapped+ where+ wrapped :: WrapParser Text m ((Either String :. f) r)+ wrapped = WrapParser composed+ composed :: Parser Text m ((Either String :. f) r)+ composed = Compose <$> p++defaultParser'+ :: ( RecAll f rs FromCell+ , Monad m+ ) + => Rec Proxy rs + -> Rec (CellParser m f) rs+defaultParser' RNil = RNil+defaultParser' (Proxy :& ps) = wrapParser fromCell :& defaultParser' ps++defaultParser+ :: forall (m :: * -> *) (f :: k -> *) (rs :: [k])+ . ( RecAll f rs FromCell+ , Monad m+ , Record rs+ )+ => Rec (CellParser m f) rs+defaultParser = defaultParser' proxyRecord+
src/Pipes/KeyValueCsv/Csv.hs view
@@ -1,50 +1,50 @@-{-| -Module : Pipes.KeyValueCsv.Csv -Copyright : (c) Marcin Mrotek, 2015 -License : BSD3 -Maintainer : marcin.jan.mrotek@gmail.com -Stability : experimental - -Parsing the main CSV body. --} - -{-# LANGUAGE - DataKinds - , FlexibleContexts - , PolyKinds - , RankNTypes - , TypeOperators - #-} - -module Pipes.KeyValueCsv.Csv - ( parseCsv - , WrapParser(..) - , wrapParser - ) where - -import Pipes.KeyValueCsv.Internal.Csv -import Pipes.KeyValueCsv.Internal.Types -import Pipes.KeyValueCsv.Common -import Pipes.KeyValueCsv.Types.Common -import Pipes.KeyValueCsv.Types.Csv - -import Control.Lens -import Data.Reflection -import Data.Vinyl -import Data.Vinyl.Functor -import qualified Pipes.Group as Group - -parseCsv - :: forall (m :: * -> *) (f :: k -> *) (rs :: [k]) (r :: *) - . ( Given Delimiter - , Monad m - ) - => CsvOptions m f rs - -> Lines m r - -> Producer (Rec (WithCsvError :. f) rs) m r --- ^Parse a stream of lines. -parseCsv options = - Group.concats - . Group.maps (parseLine $ options^.csvParser) - . unLines - +{-|+Module : Pipes.KeyValueCsv.Csv+Copyright : (c) Marcin Mrotek, 2015+License : BSD3+Maintainer : marcin.jan.mrotek@gmail.com+Stability : experimental++Parsing the main CSV body.+-}++{-# LANGUAGE + DataKinds+ , FlexibleContexts + , PolyKinds+ , RankNTypes+ , TypeOperators+ #-}++module Pipes.KeyValueCsv.Csv + ( parseCsv+ , WrapParser(..)+ , wrapParser+ ) where++import Pipes.KeyValueCsv.Internal.Csv+import Pipes.KeyValueCsv.Internal.Types+import Pipes.KeyValueCsv.Common+import Pipes.KeyValueCsv.Types.Common+import Pipes.KeyValueCsv.Types.Csv++import Control.Lens+import Data.Reflection+import Data.Vinyl+import Data.Vinyl.Functor+import qualified Pipes.Group as Group++parseCsv + :: forall (m :: * -> *) (f :: k -> *) (rs :: [k]) (r :: *)+ . ( Given Delimiter+ , Monad m+ ) + => CsvOptions m f rs+ -> Lines m r + -> Producer (Rec (WithCsvError :. f) rs) m r+-- ^Parse a stream of lines.+parseCsv options = + Group.concats + . Group.maps (parseLine $ options^.csvParser) + . unLines+
src/Pipes/KeyValueCsv/Internal.hs view
@@ -1,82 +1,82 @@-{-| -Module : Pipes.KeyValueCsv.Cell -Copyright : (c) Marcin Mrotek, 2015 -License : BSD3 -Maintainer : marcin.jan.mrotek@gmail.com -Stability : experimental - -Helper functions - --} - -{-# LANGUAGE RankNTypes #-} - -module Pipes.KeyValueCsv.Internal where - -import Pipes.KeyValueCsv.Internal.Types - -import Control.Lens -import Control.Monad.State.Strict -import Data.Char -import Data.Monoid -import Data.Text (Text) -import qualified Data.Text.Lazy as Lazy -import Data.Text.Lazy.Builder -import Pipes -import qualified Pipes.Prelude as Pipes -import Pipes.Group (FreeT(..), FreeF(..)) -import Pipes.Parse (Parser) -import qualified Pipes.Parse as Parse -import qualified Pipes.Text as PipesText - -build :: Monad m => Parser Text m Lazy.Text --- ^Build a lazy 'Text' from chunks obtained from a pipe. -build = - Parse.foldAll - (\b t -> b <> fromText t) - mempty - toLazyText - -skipSpace :: Monad m => Parser Text m () -skipSpace = zoom (PipesText.span isSpace) Parse.skipAll - -drawText :: Monad m => Parser Text m Lazy.Text -{-^ -Draw text that may be enclosed in quotes. -Skips initial whitespace. -If the first non-white character is not a quote mark, the function draws only the part of the text upto the next white character. --} -drawText = do - skipSpace - quote'm <- PipesText.peekChar - case quote'm of - Just '"' -> do - drawn <- Parse.draw *> zoom (PipesText.break isQuote) build - pure drawn - Just _ -> zoom (PipesText.break isSpace) build - Nothing -> pure $ Lazy.empty - where - isQuote = (== '"') - -breakLines' - :: Monad m - => (Text -> Bool) - -> FreeT (Line m) m r - -> FreeT (Line m) m (FreeT (Line m) m r) -{-^ -Break a 'FreeT'-delimited stream of lines into two parts, on a line that satisfies the given predicate. -All input lines up to the breaking one will be fully read, and each (not including the breaking one) will be re-'yield'ed. --} -breakLines' predicate ls = FreeT $ do - ft <- runFreeT ls - case ft of - p@(Pure _) -> pure . Pure . FreeT $ pure p - Free (Line line) -> do - (text, leftovers) <- runStateT (Lazy.toStrict <$> build) line - r <- runEffect $ leftovers >-> Pipes.drain - pure $ if predicate text - then Pure r - else Free . Line $ do - yield text - pure $ breakLines' predicate r - +{-|+Module : Pipes.KeyValueCsv.Cell+Copyright : (c) Marcin Mrotek, 2015+License : BSD3+Maintainer : marcin.jan.mrotek@gmail.com+Stability : experimental++Helper functions++-}++{-# LANGUAGE RankNTypes #-}++module Pipes.KeyValueCsv.Internal where++import Pipes.KeyValueCsv.Internal.Types++import Control.Lens+import Control.Monad.State.Strict+import Data.Char+import Data.Monoid+import Data.Text (Text)+import qualified Data.Text.Lazy as Lazy+import Data.Text.Lazy.Builder+import Pipes+import qualified Pipes.Prelude as Pipes+import Pipes.Group (FreeT(..), FreeF(..))+import Pipes.Parse (Parser)+import qualified Pipes.Parse as Parse+import qualified Pipes.Text as PipesText++build :: Monad m => Parser Text m Lazy.Text+-- ^Build a lazy 'Text' from chunks obtained from a pipe.+build = + Parse.foldAll + (\b t -> b <> fromText t) + mempty + toLazyText++skipSpace :: Monad m => Parser Text m ()+skipSpace = zoom (PipesText.span isSpace) Parse.skipAll++drawText :: Monad m => Parser Text m Lazy.Text+{-^ +Draw text that may be enclosed in quotes. +Skips initial whitespace.+If the first non-white character is not a quote mark, the function draws only the part of the text upto the next white character.+-}+drawText = do+ skipSpace+ quote'm <- PipesText.peekChar+ case quote'm of+ Just '"' -> do+ drawn <- Parse.draw *> zoom (PipesText.break isQuote) build+ pure drawn+ Just _ -> zoom (PipesText.break isSpace) build+ Nothing -> pure $ Lazy.empty+ where+ isQuote = (== '"')++breakLines'+ :: Monad m+ => (Text -> Bool)+ -> FreeT (Line m) m r+ -> FreeT (Line m) m (FreeT (Line m) m r)+{-^ +Break a 'FreeT'-delimited stream of lines into two parts, on a line that satisfies the given predicate.+All input lines up to the breaking one will be fully read, and each (not including the breaking one) will be re-'yield'ed.+-}+breakLines' predicate ls = FreeT $ do+ ft <- runFreeT ls+ case ft of+ p@(Pure _) -> pure . Pure . FreeT $ pure p+ Free (Line line) -> do+ (text, leftovers) <- runStateT (Lazy.toStrict <$> build) line+ r <- runEffect $ leftovers >-> Pipes.drain+ pure $ if predicate text+ then Pure r+ else Free . Line $ do + yield text + pure $ breakLines' predicate r+
src/Pipes/KeyValueCsv/Internal/Csv.hs view
@@ -1,100 +1,100 @@-{-| -Module : Pipes.KeyValueCsv.Internal.Csv -Copyright : (c) Marcin Mrotek, 2015 -License : BSD3 -Maintainer : marcin.jan.mrotek@gmail.com -Stability : experimental - -Helper classes and functions. --} - -{-# LANGUAGE - DataKinds - , FlexibleContexts - , FlexibleInstances - , GADTs - , MultiParamTypeClasses - , PolyKinds - , RankNTypes - , TypeOperators - #-} - -module Pipes.KeyValueCsv.Internal.Csv where - -import Pipes.KeyValueCsv.Internal.Types -import Pipes.KeyValueCsv.Common -import Pipes.KeyValueCsv.Types.Common -import Pipes.KeyValueCsv.Types.Csv - -import Control.Monad.State.Strict -import Data.Reflection (Given(..)) -import Data.Validation -import Data.Vinyl -import Data.Vinyl.Functor -import Pipes -import qualified Pipes.Prelude as Pipes -import Pipes.Group (FreeT(..), FreeF(..)) -import qualified Pipes.Group as Group -import Pipes.Parse.Tutorial() - -parseLine - :: forall (m :: * -> *) (f :: k -> *) (rs :: [k]) (r :: *) - . ( Monad m - , Given Delimiter - ) - => Rec (CellParser m f) rs - -> Line m r - -> Producer (Rec (WithCsvError :. f) rs) m r --- ^Parse a single row from a stream of text, discard the remaining characters. -parseLine parser = parseCells parser . cells - -parseCells - :: forall (m :: * -> *) (f :: k -> *) (rs :: [k]) (x :: *) - . Monad m - => Rec (CellParser m f) rs -- ^Parser record. - -> Cells m x -- ^Stream of cells. - -> Producer (Rec (WithCsvError :. f) rs) m x --- ^Parse a row of cells and pack it into a one-shot 'Producer'. -parseCells parser stream = do - (result, end) <- lift $ fromCells parser stream - yield result - pure end - -validateCell :: (Either String :. f) a -> (AccValidation [CsvError] :. f) a -validateCell (Compose e) = Compose r - where - r = case e of - Left err -> AccFailure [CellParseError err] - Right a -> AccSuccess a - -buildFromCells - :: Monad m - => Rec (CellParser m f) (r ': rs) -- ^Parser record. - -> Cells m x -- ^Stream of cells. - -> m ( Rec (WithCsvError :. f) rs -> Rec (WithCsvError :. f) (r ': rs) - , Cells m x - ) --- ^A helper function for the 'FromCells' type class. Build a 'Rec' incrementally from 'Cells'. -buildFromCells (Compose (WrapParser parser) :& _) (Cells fs) = do - ft <- runFreeT fs - case ft of - Pure r -> pure (\rs -> Compose (AccFailure [MissingCell]) :& rs, Cells $ pure r) - Free (Cell cell) -> do - (result,leftovers) <- runStateT parser cell - end <- runEffect $ leftovers >-> Pipes.drain - pure (\rs -> validateCell result :& rs, Cells end) - -fromCells - :: Monad m - => Rec (CellParser m f) rs -- ^Parser record. - -> Cells m x -- ^Stream of cells. - -> m (Rec (WithCsvError :. f) rs, x) --- ^Parse a row of cells. For more information about parsing with Pipes, see "Pipes.Parse.Tutorial". -fromCells RNil (Cells fs) = do - end <- runEffect $ Group.concats ( Group.maps unCell fs) >-> Pipes.drain - pure (RNil, end) -fromCells parsers@(_ :& ps) stream = do - (run, remaining) <- buildFromCells parsers stream - (result, end) <- fromCells ps remaining - pure (run result, end) - +{-|+Module : Pipes.KeyValueCsv.Internal.Csv+Copyright : (c) Marcin Mrotek, 2015+License : BSD3+Maintainer : marcin.jan.mrotek@gmail.com+Stability : experimental++Helper classes and functions.+-}++{-# LANGUAGE+ DataKinds+ , FlexibleContexts+ , FlexibleInstances+ , GADTs+ , MultiParamTypeClasses+ , PolyKinds+ , RankNTypes+ , TypeOperators+ #-}++module Pipes.KeyValueCsv.Internal.Csv where++import Pipes.KeyValueCsv.Internal.Types+import Pipes.KeyValueCsv.Common+import Pipes.KeyValueCsv.Types.Common+import Pipes.KeyValueCsv.Types.Csv++import Control.Monad.State.Strict+import Data.Reflection (Given(..))+import Data.Validation+import Data.Vinyl+import Data.Vinyl.Functor+import Pipes+import qualified Pipes.Prelude as Pipes+import Pipes.Group (FreeT(..), FreeF(..))+import qualified Pipes.Group as Group+import Pipes.Parse.Tutorial()++parseLine + :: forall (m :: * -> *) (f :: k -> *) (rs :: [k]) (r :: *)+ . ( Monad m+ , Given Delimiter+ ) + => Rec (CellParser m f) rs+ -> Line m r + -> Producer (Rec (WithCsvError :. f) rs) m r+-- ^Parse a single row from a stream of text, discard the remaining characters.+parseLine parser = parseCells parser . cells++parseCells + :: forall (m :: * -> *) (f :: k -> *) (rs :: [k]) (x :: *)+ . Monad m+ => Rec (CellParser m f) rs -- ^Parser record.+ -> Cells m x -- ^Stream of cells.+ -> Producer (Rec (WithCsvError :. f) rs) m x+-- ^Parse a row of cells and pack it into a one-shot 'Producer'.+parseCells parser stream = do+ (result, end) <- lift $ fromCells parser stream + yield result+ pure end++validateCell :: (Either String :. f) a -> (Validation [CsvError] :. f) a+validateCell (Compose e) = Compose r+ where+ r = case e of + Left err -> Failure [CellParseError err]+ Right a -> Success a++buildFromCells + :: Monad m + => Rec (CellParser m f) (r ': rs) -- ^Parser record.+ -> Cells m x -- ^Stream of cells.+ -> m ( Rec (WithCsvError :. f) rs -> Rec (WithCsvError :. f) (r ': rs)+ , Cells m x+ )+-- ^A helper function for the 'FromCells' type class. Build a 'Rec' incrementally from 'Cells'.+buildFromCells (Compose (WrapParser parser) :& _) (Cells fs) = do+ ft <- runFreeT fs + case ft of+ Pure r -> pure (\rs -> Compose (Failure [MissingCell]) :& rs, Cells $ pure r)+ Free (Cell cell) -> do+ (result,leftovers) <- runStateT parser cell+ end <- runEffect $ leftovers >-> Pipes.drain+ pure (\rs -> validateCell result :& rs, Cells end)++fromCells + :: Monad m + => Rec (CellParser m f) rs -- ^Parser record.+ -> Cells m x -- ^Stream of cells.+ -> m (Rec (WithCsvError :. f) rs, x)+-- ^Parse a row of cells. For more information about parsing with Pipes, see "Pipes.Parse.Tutorial".+fromCells RNil (Cells fs) = do+ end <- runEffect $ Group.concats ( Group.maps unCell fs) >-> Pipes.drain+ pure (RNil, end)+fromCells parsers@(_ :& ps) stream = do+ (run, remaining) <- buildFromCells parsers stream+ (result, end) <- fromCells ps remaining + pure (run result, end)+
src/Pipes/KeyValueCsv/Internal/KeyValue.hs view
@@ -58,8 +58,8 @@ pure (Just text, Cells r) missing :: forall (f :: k -> *) (rs :: [k]). Record rs => Rec (WithKeyValueError :. f) rs --- ^An empty record filled with 'AccFailure' 'MissingValue'. -missing = recPure (Compose $ AccFailure [MissingValue]) --missing' proxyRecord +-- ^An empty record filled with 'Failure' 'MissingValue'. +missing = recPure (Compose $ Failure [MissingValue]) --missing' proxyRecord parseKeyValue :: forall (m :: * -> *) (f :: k -> *) (rs :: [k]) (r :: *) @@ -72,7 +72,7 @@ -> Set Text -- ^Name set. -> Line m r -- ^Line to parse. -> Producer (Rec (WithKeyValueError :. f) rs) m r --- ^Parse a single line into an one-shot producer of records with at most one 'AccSuccess' field. +-- ^Parse a single line into an one-shot producer of records with at most one 'Success' field. parseKeyValue parser names names'set line = do (key'm, Cells cs) <- lift $ runStateT (fmap Lazy.toStrict <$> drawCell) $ cells line remaining'f <- lift $ runFreeT cs @@ -100,7 +100,7 @@ -> Rec (Const Text) rs -- ^Key name record. -> Rec (WithKeyValueError :. f) rs -- ^Current parsing result. -> m (Rec (WithKeyValueError :. f) rs, x) --- ^Parse a key-value pair into a record with at most one 'AccSuccess' field. +-- ^Parse a key-value pair into a record with at most one 'Success' field. fromKeyValues (_,p) RNil RNil RNil = do x <- runEffect $ p >-> Pipes.drain pure (RNil, x) @@ -111,8 +111,8 @@ result <- runEffect $ leftovers >-> Pipes.drain let validated = case parsed of - Left err -> AccFailure [ValueParsingError err] - Right a -> AccSuccess a + Left err -> Failure [ValueParsingError err] + Right a -> Success a pure (Compose validated :& rs, result) else do (remaining, result) <- fromKeyValues pair ps ks rs
src/Pipes/KeyValueCsv/Internal/Names.hs view
@@ -1,30 +1,30 @@-{-| -Module : Pipes.KeyValueCsv.Internal.Names -Copyright : (c) Marcin Mrotek, 2015 -License : BSD3 -Maintainer : marcin.jan.mrotek@gmail.com -Stability : experimental - -Default key name generation by printing the field label type names. --} - -{-# LANGUAGE - DataKinds - , GADTs - , PolyKinds - #-} - -module Pipes.KeyValueCsv.Internal.Names where - -import Data.Vinyl -import Data.Vinyl.Functor -import Data.Vinyl.Utils.Field -import Data.Text (Text) -import qualified Data.Text as Text -import Data.Typeable - -names :: Rec (DictProxy Typeable) rs -> Rec (Const Text) rs --- ^Construct a record of 'Text' from a record of 'Proxy'ies enriched with 'Typeable' constraints. -names RNil = RNil -names (p@DictProxy :& ps) = (Const . Text.pack . show . typeRep $ getProxy p) :& names ps - +{-|+Module : Pipes.KeyValueCsv.Internal.Names+Copyright : (c) Marcin Mrotek, 2015+License : BSD3+Maintainer : marcin.jan.mrotek@gmail.com+Stability : experimental++Default key name generation by printing the field label type names.+-}++{-# LANGUAGE+ DataKinds+ , GADTs+ , PolyKinds+ #-}++module Pipes.KeyValueCsv.Internal.Names where++import Data.Vinyl+import Data.Vinyl.Functor+import Data.Vinyl.Utils.Field+import Data.Text (Text)+import qualified Data.Text as Text+import Data.Typeable++names :: Rec (DictProxy Typeable) rs -> Rec (Const Text) rs+-- ^Construct a record of 'Text' from a record of 'Proxy'ies enriched with 'Typeable' constraints.+names RNil = RNil+names (p@DictProxy :& ps) = (Const . Text.pack . show . typeRep $ getProxy p) :& names ps+
src/Pipes/KeyValueCsv/Internal/Types.hs view
@@ -1,36 +1,36 @@-{-| -Module : Pipes.KeyValueCsv.Csv -Copyright : (c) Marcin Mrotek, 2015 -License : BSD3 -Maintainer : marcin.jan.mrotek@gmail.com -Stability : experimental - -Abstract types. --} - -{-# LANGUAGE GeneralizedNewtypeDeriving #-} - -module Pipes.KeyValueCsv.Internal.Types where - -import Data.Text (Text) -import Pipes -import Pipes.Group (FreeT) - --- |A wrapped 'Char' that has the explicit puropse of delimiting cells. -newtype Delimiter = Delimiter { getDelimiter :: Char } - --- |A single line. Internally, a stream of text. -newtype Line m r = Line { unLine :: Producer Text m r } - deriving (Functor, Applicative, Monad) --- |A stream of lines. -newtype Lines m r = Lines { unLines :: FreeT (Line m) m r } - deriving (Functor, Applicative, Monad) - --- |A single cell. Internally, a stream of text. -newtype Cell m r = Cell { unCell :: Producer Text m r } - deriving (Functor, Applicative, Monad) --- |A stream of cells. -newtype Cells m r = Cells { unCells :: FreeT (Cell m) m r } - deriving (Functor, Applicative, Monad) - - +{-|+Module : Pipes.KeyValueCsv.Csv+Copyright : (c) Marcin Mrotek, 2015+License : BSD3+Maintainer : marcin.jan.mrotek@gmail.com+Stability : experimental++Abstract types.+-}++{-# LANGUAGE GeneralizedNewtypeDeriving #-}++module Pipes.KeyValueCsv.Internal.Types where++import Data.Text (Text)+import Pipes+import Pipes.Group (FreeT)++-- |A wrapped 'Char' that has the explicit puropse of delimiting cells.+newtype Delimiter = Delimiter { getDelimiter :: Char }++-- |A single line. Internally, a stream of text.+newtype Line m r = Line { unLine :: Producer Text m r }+ deriving (Functor, Applicative, Monad)+-- |A stream of lines.+newtype Lines m r = Lines { unLines :: FreeT (Line m) m r }+ deriving (Functor, Applicative, Monad)++-- |A single cell. Internally, a stream of text.+newtype Cell m r = Cell { unCell :: Producer Text m r }+ deriving (Functor, Applicative, Monad)+-- |A stream of cells.+newtype Cells m r = Cells { unCells :: FreeT (Cell m) m r }+ deriving (Functor, Applicative, Monad)++
src/Pipes/KeyValueCsv/KeyValue.hs view
@@ -46,7 +46,7 @@ => KeyValueOptions m f rs -- ^Options -> Lines m r -- ^Stream of lines -> m (Rec (WithKeyValueError :. f) rs, r) --- ^Parse lines into a stream of records with at most one 'AccSuccess' field. +-- ^Parse lines into a stream of records with at most one 'Success' field. parseKeyValues options = foldHeader . Group.concats @@ -71,12 +71,12 @@ joinRecs (Compose va :& as) (Compose vb :& bs) = Compose new :& joinRecs as bs where new = case (va, vb) of - (AccSuccess a, AccFailure [MissingValue]) -> AccSuccess a - (AccFailure [MissingValue], AccSuccess b) -> AccSuccess b - (AccSuccess _, AccSuccess _) -> AccFailure [MultipleValues] - (AccSuccess _, AccFailure f) -> AccFailure f - (AccFailure f, AccSuccess _) -> AccFailure f - (AccFailure [MissingValue], AccFailure f) -> AccFailure f - (AccFailure f, AccFailure [MissingValue]) -> AccFailure f - (AccFailure f1, AccFailure f2) -> AccFailure $ MultipleValues : (f1++f2) + (Success a, Failure [MissingValue]) -> Success a + (Failure [MissingValue], Success b) -> Success b + (Success _, Success _) -> Failure [MultipleValues] + (Success _, Failure f) -> Failure f + (Failure f, Success _) -> Failure f + (Failure [MissingValue], Failure f) -> Failure f + (Failure f, Failure [MissingValue]) -> Failure f + (Failure f1, Failure f2) -> Failure $ MultipleValues : (f1++f2)
src/Pipes/KeyValueCsv/Names.hs view
@@ -1,35 +1,35 @@-{-| -Module : Pipes.KeyValueCsv.Names -Copyright : (c) Marcin Mrotek, 2015 -License : BSD3 -Maintainer : marcin.jan.mrotek@gmail.com -Stability : experimental - -Default key name generation by printing the field label type names. --} - -{-# LANGUAGE - DataKinds - , ExplicitForAll - , PolyKinds - #-} - -module Pipes.KeyValueCsv.Names - ( module Pipes.KeyValueCsv.Names - , FieldAll - , Record - ) where - -import qualified Pipes.KeyValueCsv.Internal.Names as Internal - -import Data.Proxy -import Data.Vinyl -import Data.Vinyl.Functor -import Data.Vinyl.Utils.Field -import Data.Vinyl.Utils.Proxy -import Data.Text (Text) -import Data.Typeable - -names :: forall (rs :: [k]). (FieldAll rs Typeable, Record rs) => Rec (Const Text) rs -names = Internal.names $ reifyFieldConstraint (Proxy :: Proxy Typeable) proxyRecord - +{-|+Module : Pipes.KeyValueCsv.Names+Copyright : (c) Marcin Mrotek, 2015+License : BSD3+Maintainer : marcin.jan.mrotek@gmail.com+Stability : experimental++Default key name generation by printing the field label type names.+-}++{-# LANGUAGE + DataKinds+ , ExplicitForAll+ , PolyKinds+ #-}++module Pipes.KeyValueCsv.Names+ ( module Pipes.KeyValueCsv.Names+ , FieldAll+ , Record+ ) where++import qualified Pipes.KeyValueCsv.Internal.Names as Internal++import Data.Proxy+import Data.Vinyl+import Data.Vinyl.Functor+import Data.Vinyl.Utils.Field+import Data.Vinyl.Utils.Proxy+import Data.Text (Text)+import Data.Typeable++names :: forall (rs :: [k]). (FieldAll rs Typeable, Record rs) => Rec (Const Text) rs+names = Internal.names $ reifyFieldConstraint (Proxy :: Proxy Typeable) proxyRecord+
src/Pipes/KeyValueCsv/Types.hs view
@@ -1,54 +1,54 @@-{-| -Module : Pipes.KeyValueCsv.Types -Copyright : (c) Marcin Mrotek, 2015 -License : BSD3 -Maintainer : marcin.jan.mrotek@gmail.com -Stability : experimental - -Types used by the key-vale + CSV processing part. --} - -{-# LANGUAGE - DataKinds - , FlexibleContexts - , PolyKinds - , RankNTypes - , TemplateHaskell - #-} - -module Pipes.KeyValueCsv.Types - ( module Pipes.KeyValueCsv.Types - , module Pipes.KeyValueCsv.Types.Common - , module Pipes.KeyValueCsv.Types.Csv - , module Pipes.KeyValueCsv.Types.KeyValue - ) where - - -import Pipes.KeyValueCsv.Types.Common -import Pipes.KeyValueCsv.Types.Csv -import Pipes.KeyValueCsv.Types.KeyValue - -import Control.Lens -import Data.Default.Class -import Data.Text (Text) - --- |Options for the whole processing. -data Options (m :: * -> *) (f :: k -> *) (g :: j -> *) (hs :: [k]) (rs :: [j]) = Options - { _kvOptions :: KeyValueOptions m f hs - , _csvOptions :: CsvOptions m g rs - , _predicate :: Text -> Bool - , _delimiter :: Char - } - -makeLenses ''Options - -instance ( Default (KeyValueOptions m f hs) - , Default (CsvOptions m g rs) - ) => Default (Options m f g hs rs) where - def = Options - { _kvOptions = def - , _csvOptions = def - , _predicate = const False - , _delimiter = ',' - } - +{-|+Module : Pipes.KeyValueCsv.Types+Copyright : (c) Marcin Mrotek, 2015+License : BSD3+Maintainer : marcin.jan.mrotek@gmail.com+Stability : experimental++Types used by the key-vale + CSV processing part.+-}++{-# LANGUAGE + DataKinds+ , FlexibleContexts+ , PolyKinds+ , RankNTypes+ , TemplateHaskell + #-}++module Pipes.KeyValueCsv.Types+ ( module Pipes.KeyValueCsv.Types+ , module Pipes.KeyValueCsv.Types.Common+ , module Pipes.KeyValueCsv.Types.Csv+ , module Pipes.KeyValueCsv.Types.KeyValue+ ) where+++import Pipes.KeyValueCsv.Types.Common+import Pipes.KeyValueCsv.Types.Csv+import Pipes.KeyValueCsv.Types.KeyValue++import Control.Lens+import Data.Default.Class+import Data.Text (Text)++-- |Options for the whole processing. +data Options (m :: * -> *) (f :: k -> *) (g :: j -> *) (hs :: [k]) (rs :: [j]) = Options+ { _kvOptions :: KeyValueOptions m f hs+ , _csvOptions :: CsvOptions m g rs+ , _predicate :: Text -> Bool+ , _delimiter :: Char+ }++makeLenses ''Options++instance ( Default (KeyValueOptions m f hs)+ , Default (CsvOptions m g rs)+ ) => Default (Options m f g hs rs) where+ def = Options+ { _kvOptions = def+ , _csvOptions = def+ , _predicate = const False+ , _delimiter = ','+ }+
src/Pipes/KeyValueCsv/Types/Common.hs view
@@ -1,40 +1,40 @@-{-| -Module : Pipes.KeyValueCsv.Types.Common -Copyright : (c) Marcin Mrotek, 2015 -License : BSD3 -Maintainer : marcin.jan.mrotek@gmail.com -Stability : experimental - -Types common to both CSV and key-value part of the library, and reexports of abstract types. --} - -{-# LANGUAGE - DataKinds - , GADTs - , FlexibleContexts - , ScopedTypeVariables - , PolyKinds - , RankNTypes - , TypeOperators - #-} - -module Pipes.KeyValueCsv.Types.Common - ( Line - , Lines - , Cell - , Cells - , Delimiter - , module Pipes.KeyValueCsv.Types.Common - ) where - -import Pipes.KeyValueCsv.Internal.Types - -import Data.Vinyl.Functor -import Data.Text (Text) -import Pipes.Parse (Parser) - --- |As 'Parser' is a type synonym, it can't be partially applied as an argument for 'Rec'. -newtype WrapParser (a :: *) (m :: * -> *) (r :: *) = WrapParser { unwrapParser :: Parser a m r } - --- |Shorthand for a 'Parser' of 'Text' that can return a 'String' error a'la "Data.Text.Read". -type CellParser (m :: * -> *) (f :: k -> *) = WrapParser Text m :. (Either String :. f) +{-|+Module : Pipes.KeyValueCsv.Types.Common+Copyright : (c) Marcin Mrotek, 2015+License : BSD3+Maintainer : marcin.jan.mrotek@gmail.com+Stability : experimental++Types common to both CSV and key-value part of the library, and reexports of abstract types.+-}++{-# LANGUAGE+ DataKinds+ , GADTs+ , FlexibleContexts+ , ScopedTypeVariables+ , PolyKinds+ , RankNTypes+ , TypeOperators+ #-}++module Pipes.KeyValueCsv.Types.Common+ ( Line+ , Lines+ , Cell+ , Cells+ , Delimiter+ , module Pipes.KeyValueCsv.Types.Common+ ) where++import Pipes.KeyValueCsv.Internal.Types ++import Data.Vinyl.Functor+import Data.Text (Text)+import Pipes.Parse (Parser)++-- |As 'Parser' is a type synonym, it can't be partially applied as an argument for 'Rec'.+newtype WrapParser (a :: *) (m :: * -> *) (r :: *) = WrapParser { unwrapParser :: Parser a m r }++-- |Shorthand for a 'Parser' of 'Text' that can return a 'String' error a'la "Data.Text.Read".+type CellParser (m :: * -> *) (f :: k -> *) = WrapParser Text m :. (Either String :. f)
src/Pipes/KeyValueCsv/Types/Csv.hs view
@@ -1,52 +1,52 @@-{-| -Module : Pipes.KeyValueCsv.Types.Csv -Copyright : (c) Marcin Mrotek, 2015 -License : BSD3 -Maintainer : marcin.jan.mrotek@gmail.com -Stability : experimental - -Types used by the CSV part of the library. --} - -{-# LANGUAGE - DataKinds - , GADTs - , RankNTypes - , PolyKinds - , UndecidableInstances - #-} - -module Pipes.KeyValueCsv.Types.Csv where - -import Pipes.KeyValueCsv.Cell -import Pipes.KeyValueCsv.Common -import Pipes.KeyValueCsv.Types.Common - -import Control.Lens -import Data.Vinyl -import Data.Vinyl.TypeLevel -import Data.Vinyl.Utils.Proxy -import Data.Default.Class -import Data.Validation - -data CsvOptions (m :: * -> *) (f :: k -> *) (rs :: [k]) = CsvOptions - { _csvParser :: Rec (CellParser m f) rs - } - -csvParser - :: forall (m :: * -> *) (f :: k -> *) (rs :: [k]) - . Lens' (CsvOptions m f rs) (Rec (CellParser m f) rs) -csvParser = lens _csvParser $ \o p -> o {_csvParser = p} - -instance (RecAll f rs FromCell, Record rs, Monad m) => Default (CsvOptions m f rs) where - def = CsvOptions defaultParser - --- |An error that occurred during the parsing of a row. -data CsvError - = CellParseError String -- ^Error as returned by "Text.Read". - | MissingCell - deriving (Show, Eq) - --- |Shorthand for functions that can return 'CsvError's. -type WithCsvError = AccValidation [CsvError] - +{-|+Module : Pipes.KeyValueCsv.Types.Csv+Copyright : (c) Marcin Mrotek, 2015+License : BSD3+Maintainer : marcin.jan.mrotek@gmail.com+Stability : experimental++Types used by the CSV part of the library.+-}++{-# LANGUAGE + DataKinds+ , GADTs+ , RankNTypes+ , PolyKinds+ , UndecidableInstances+ #-}++module Pipes.KeyValueCsv.Types.Csv where++import Pipes.KeyValueCsv.Cell+import Pipes.KeyValueCsv.Common +import Pipes.KeyValueCsv.Types.Common ++import Control.Lens+import Data.Vinyl+import Data.Vinyl.TypeLevel+import Data.Vinyl.Utils.Proxy+import Data.Default.Class+import Data.Validation++data CsvOptions (m :: * -> *) (f :: k -> *) (rs :: [k]) = CsvOptions+ { _csvParser :: Rec (CellParser m f) rs+ }++csvParser + :: forall (m :: * -> *) (f :: k -> *) (rs :: [k]) + . Lens' (CsvOptions m f rs) (Rec (CellParser m f) rs)+csvParser = lens _csvParser $ \o p -> o {_csvParser = p}++instance (RecAll f rs FromCell, Record rs, Monad m) => Default (CsvOptions m f rs) where+ def = CsvOptions defaultParser ++-- |An error that occurred during the parsing of a row.+data CsvError+ = CellParseError String -- ^Error as returned by "Text.Read".+ | MissingCell + deriving (Show, Eq)++-- |Shorthand for functions that can return 'CsvError's.+type WithCsvError = Validation [CsvError]+
src/Pipes/KeyValueCsv/Types/KeyValue.hs view
@@ -1,59 +1,59 @@-{-| -Module : Pipes.KeyValueCsv.Types.KeyValue -Copyright : (c) Marcin Mrotek, 2015 -License : BSD3 -Maintainer : marcin.jan.mrotek@gmail.com -Stability : experimental - -Types used by the key-value part of the library. --} - -{-# LANGUAGE - DataKinds - , PolyKinds - , TemplateHaskell - , UndecidableInstances - #-} - -module Pipes.KeyValueCsv.Types.KeyValue where - -import Pipes.KeyValueCsv.Cell -import Pipes.KeyValueCsv.Common -import Pipes.KeyValueCsv.Names -import Pipes.KeyValueCsv.Types.Common - -import Control.Lens hiding (Const) -import Data.Default.Class -import Data.Typeable -import Data.Vinyl -import Data.Vinyl.Functor -import Data.Vinyl.TypeLevel -import Data.Validation - --- |An error that occured during the parsing of the header. -data KeyValueError - = MissingValue -- ^A value is missing in the header. - | MultipleValues -- ^A value is given multiple times. - | ValueParsingError String -- ^Parsing error as returned by "Data.Text.Lazy.Read". - deriving (Show, Eq) - --- |Shorthand for functions that can return 'KeyValueError's. -type WithKeyValueError = AccValidation [KeyValueError] - -data KeyValueOptions (m :: * -> *) (f :: k -> *) (rs :: [k]) = KeyValueOptions - { _kvParser :: Rec (CellParser m f) rs - , _keyNames :: Rec (Const Text) rs - } - -makeLenses ''KeyValueOptions - -instance ( RecAll f rs FromCell - , FieldAll rs Typeable - , Record rs - , Monad m - ) => Default (KeyValueOptions m f rs) where - def = KeyValueOptions - { _kvParser = defaultParser - , _keyNames = names - } - +{-|+Module : Pipes.KeyValueCsv.Types.KeyValue+Copyright : (c) Marcin Mrotek, 2015+License : BSD3+Maintainer : marcin.jan.mrotek@gmail.com+Stability : experimental++Types used by the key-value part of the library.+-}++{-# LANGUAGE + DataKinds+ , PolyKinds+ , TemplateHaskell + , UndecidableInstances+ #-}++module Pipes.KeyValueCsv.Types.KeyValue where++import Pipes.KeyValueCsv.Cell+import Pipes.KeyValueCsv.Common+import Pipes.KeyValueCsv.Names+import Pipes.KeyValueCsv.Types.Common ++import Control.Lens hiding (Const)+import Data.Default.Class+import Data.Typeable+import Data.Vinyl+import Data.Vinyl.Functor+import Data.Vinyl.TypeLevel+import Data.Validation++-- |An error that occured during the parsing of the header.+data KeyValueError + = MissingValue -- ^A value is missing in the header.+ | MultipleValues -- ^A value is given multiple times.+ | ValueParsingError String -- ^Parsing error as returned by "Data.Text.Lazy.Read".+ deriving (Show, Eq)++-- |Shorthand for functions that can return 'KeyValueError's.+type WithKeyValueError = Validation [KeyValueError]++data KeyValueOptions (m :: * -> *) (f :: k -> *) (rs :: [k]) = KeyValueOptions+ { _kvParser :: Rec (CellParser m f) rs+ , _keyNames :: Rec (Const Text) rs+ }++makeLenses ''KeyValueOptions++instance ( RecAll f rs FromCell+ , FieldAll rs Typeable+ , Record rs+ , Monad m+ ) => Default (KeyValueOptions m f rs) where+ def = KeyValueOptions + { _kvParser = defaultParser+ , _keyNames = names+ }+
test/Test/Common.hs view
@@ -1,46 +1,46 @@-{-# LANGUAGE - DataKinds - , FlexibleContexts - , FlexibleInstances - , TypeOperators - #-} - -module Test.Common where - -import Data.Functor.Contravariant -import Data.List -import Data.Text.Lazy.Builder -import Data.Vinyl -import Data.Vinyl.Functor -import Data.Vinyl.Utils.Operator - -type Id = Identity - -buildDelimited - :: Buildable rs - => Char - -> Rec Id rs - -> Builder -buildDelimited delim = - mconcat - . intersperse (singleton delim) - . build - -build - :: Buildable rs - => Rec Id rs - -> [Builder] -build r = - map getIdentity - . recordToList - $ builder \$\ r - -class Buildable rs where - builder :: Rec (Op Builder) rs - -instance Buildable '[] where - builder = RNil - -instance (Buildable rs, Show r) => Buildable (r ': rs) where - builder = Op (fromString . show) :& builder - +{-# LANGUAGE+ DataKinds+ , FlexibleContexts+ , FlexibleInstances+ , TypeOperators+ #-}++module Test.Common where++import Data.Functor.Contravariant+import Data.List+import Data.Text.Lazy.Builder+import Data.Vinyl+import Data.Vinyl.Functor+import Data.Vinyl.Utils.Operator++type Id = Identity++buildDelimited+ :: Buildable rs + => Char + -> Rec Id rs + -> Builder+buildDelimited delim = + mconcat + . intersperse (singleton delim)+ . build++build + :: Buildable rs + => Rec Id rs + -> [Builder]+build r = + map getIdentity+ . recordToList + $ builder \$\ r++class Buildable rs where+ builder :: Rec (Op Builder) rs++instance Buildable '[] where+ builder = RNil++instance (Buildable rs, Show r) => Buildable (r ': rs) where+ builder = Op (fromString . show) :& builder+
test/Test/Csv.hs view
@@ -1,58 +1,58 @@-{-# LANGUAGE - DataKinds - , FlexibleContexts - , TemplateHaskell - #-} - -module Test.Csv where - -import Prelude hiding (lines) -import Test.Common -import Test.Instances () -import Test.Types - -import Pipes.KeyValueCsv.Common -import Pipes.KeyValueCsv.Csv -import Pipes.KeyValueCsv.Types - -import Control.Lens hiding (Const) -import qualified Data.Functor.Identity as Transformers -import Data.Default.Class -import Data.List hiding (lines) -import qualified Data.Text as Text -import Data.Text.Lazy.Builder -import Data.Reflection -import Data.Validation -import Data.Vinyl -import Data.Vinyl.Utils.Compose -import Test.QuickCheck -import qualified Pipes.Prelude as Pipes -import Pipes.Text (fromLazy) - -delim :: Char -delim = ',' - -type TestFields = '[Int, Alphabetic, Quoted] - -prop_read :: [Rec Id TestFields] -> Property -prop_read rs = map pure rs === useDelimiter delim parsed - & counterexample (concat. map Text.unpack $ Pipes.toList pipe) - where - pipe :: Producer Text Transformers.Identity () - pipe = - fromLazy - . toLazyText - . mconcat - . intersperse (singleton '\n') - $ map (buildDelimited delim) rs - parsed :: Given Delimiter => [AccValidation [CsvError] (Rec Id TestFields)] - parsed = - map rtraverse1 - . Pipes.toList - . parseCsv def - $ lines pipe - -return [] - -test :: IO Bool -test = $quickCheckAll +{-# LANGUAGE + DataKinds + , FlexibleContexts+ , TemplateHaskell+ #-}++module Test.Csv where++import Prelude hiding (lines)+import Test.Common+import Test.Instances ()+import Test.Types++import Pipes.KeyValueCsv.Common+import Pipes.KeyValueCsv.Csv+import Pipes.KeyValueCsv.Types++import Control.Lens hiding (Const)+import qualified Data.Functor.Identity as Transformers+import Data.Default.Class+import Data.List hiding (lines)+import qualified Data.Text as Text+import Data.Text.Lazy.Builder+import Data.Reflection+import Data.Validation+import Data.Vinyl+import Data.Vinyl.Utils.Compose+import Test.QuickCheck+import qualified Pipes.Prelude as Pipes+import Pipes.Text (fromLazy)++delim :: Char+delim = ','++type TestFields = '[Int, Alphabetic, Quoted]++prop_read :: [Rec Id TestFields] -> Property+prop_read rs = map pure rs === useDelimiter delim parsed+ & counterexample (concat. map Text.unpack $ Pipes.toList pipe)+ where+ pipe :: Producer Text Transformers.Identity ()+ pipe = + fromLazy + . toLazyText + . mconcat + . intersperse (singleton '\n')+ $ map (buildDelimited delim) rs+ parsed :: Given Delimiter => [AccValidation [CsvError] (Rec Id TestFields)]+ parsed =+ map rtraverse1+ . Pipes.toList+ . parseCsv def+ $ lines pipe++return []++test :: IO Bool+test = $quickCheckAll
test/Test/KeyValue.hs view
@@ -1,67 +1,67 @@-{-# LANGUAGE - DataKinds - , FlexibleContexts - , OverloadedStrings - , TemplateHaskell - #-} - -module Test.KeyValue where - -import Prelude hiding (lines) -import Test.Common -import Test.Instances () -import Test.Types - -import Pipes.KeyValueCsv.Common -import Pipes.KeyValueCsv.KeyValue -import Pipes.KeyValueCsv.Types - -import Control.Lens hiding (Const) -import qualified Data.Functor.Identity as Transformers -import Data.Default.Class -import Data.List hiding (lines) -import Data.Monoid -import qualified Data.Text as Text -import Data.Text.Lazy.Builder -import Data.Reflection -import Data.Validation -import Data.Vinyl -import Data.Vinyl.Functor -import Data.Vinyl.Utils.Compose -import Test.QuickCheck -import qualified Pipes.Prelude as Pipes -import Pipes.Text (fromLazy) - -delim :: Char -delim = ',' - -type TestFields = '[Int, Alphabetic, Quoted] - -prop_read :: Rec Id TestFields -> Property -prop_read r = pure r === useDelimiter delim parsed - & counterexample (concat. map Text.unpack $ Pipes.toList pipe) - where - names :: Rec (Const Text) TestFields - names = Const "foo" :& Const "bar" :& Const "baz" :& RNil - pipe :: Producer Text Transformers.Identity () - pipe = - fromLazy - . toLazyText - . mconcat - . intersperse (singleton '\n') - . zipWith keyValue (map fromText $ recordToList names) - $ build r - keyValue k v = k <> singleton delim <> v - parsed :: Given Delimiter => AccValidation [KeyValueError] (Rec Id TestFields) - parsed = - rtraverse1 - . fst - . Transformers.runIdentity - . parseKeyValues (def & keyNames .~ names) - $ lines pipe - -return [] - -test :: IO Bool -test = $quickCheckAll - +{-# LANGUAGE+ DataKinds+ , FlexibleContexts+ , OverloadedStrings+ , TemplateHaskell+ #-}++module Test.KeyValue where++import Prelude hiding (lines)+import Test.Common+import Test.Instances ()+import Test.Types++import Pipes.KeyValueCsv.Common+import Pipes.KeyValueCsv.KeyValue+import Pipes.KeyValueCsv.Types++import Control.Lens hiding (Const)+import qualified Data.Functor.Identity as Transformers+import Data.Default.Class+import Data.List hiding (lines)+import Data.Monoid+import qualified Data.Text as Text+import Data.Text.Lazy.Builder+import Data.Reflection+import Data.Validation+import Data.Vinyl+import Data.Vinyl.Functor+import Data.Vinyl.Utils.Compose+import Test.QuickCheck+import qualified Pipes.Prelude as Pipes+import Pipes.Text (fromLazy)++delim :: Char+delim = ','++type TestFields = '[Int, Alphabetic, Quoted]++prop_read :: Rec Id TestFields -> Property+prop_read r = pure r === useDelimiter delim parsed+ & counterexample (concat. map Text.unpack $ Pipes.toList pipe)+ where+ names :: Rec (Const Text) TestFields+ names = Const "foo" :& Const "bar" :& Const "baz" :& RNil+ pipe :: Producer Text Transformers.Identity ()+ pipe = + fromLazy + . toLazyText + . mconcat + . intersperse (singleton '\n')+ . zipWith keyValue (map fromText $ recordToList names) + $ build r+ keyValue k v = k <> singleton delim <> v+ parsed :: Given Delimiter => AccValidation [KeyValueError] (Rec Id TestFields)+ parsed =+ rtraverse1+ . fst+ . Transformers.runIdentity+ . parseKeyValues (def & keyNames .~ names)+ $ lines pipe++return []++test :: IO Bool+test = $quickCheckAll+
test/Test/Types.hs view
@@ -1,42 +1,42 @@-module Test.Types where - -import Pipes.KeyValueCsv.Cell - -import Data.Char -import Test.QuickCheck - -newtype Alphabetic = Alphabetic { getAlphabetic :: String } - deriving (Eq) - -instance Show Alphabetic where - show (Alphabetic a) = show a - -alphabetLower :: String -alphabetLower = "abcdefghijklmnopqrstuvwxyz" - -alphabet :: String -alphabet = alphabetLower ++ map toUpper alphabetLower - -instance FromCell Alphabetic where - fromCell = fmap Alphabetic <$> fromCell - -instance Arbitrary Alphabetic where - arbitrary = Alphabetic <$> listOf1 (elements alphabet) - shrink (Alphabetic a) = Alphabetic <$> shrink a - -newtype Quoted = Quoted { quoted :: String } - deriving (Eq) - -instance Show Quoted where - show (Quoted q) = q - -quote :: String -> String -quote s = "\"" ++ s ++ "\"" - -instance Arbitrary Quoted where - arbitrary = Quoted . quote <$> listOf1 (elements $ alphabet ++ " ") - shrink (Quoted q) = Quoted . quote <$> shrink contents - where contents = init $ tail q - -instance FromCell Quoted where - fromCell = fmap (Quoted . quote) <$> fromCell +module Test.Types where++import Pipes.KeyValueCsv.Cell++import Data.Char+import Test.QuickCheck++newtype Alphabetic = Alphabetic { getAlphabetic :: String }+ deriving (Eq)++instance Show Alphabetic where+ show (Alphabetic a) = show a++alphabetLower :: String+alphabetLower = "abcdefghijklmnopqrstuvwxyz"++alphabet :: String+alphabet = alphabetLower ++ map toUpper alphabetLower++instance FromCell Alphabetic where+ fromCell = fmap Alphabetic <$> fromCell++instance Arbitrary Alphabetic where+ arbitrary = Alphabetic <$> listOf1 (elements alphabet)+ shrink (Alphabetic a) = Alphabetic <$> shrink a++newtype Quoted = Quoted { quoted :: String }+ deriving (Eq)++instance Show Quoted where+ show (Quoted q) = q++quote :: String -> String +quote s = "\"" ++ s ++ "\"" ++instance Arbitrary Quoted where+ arbitrary = Quoted . quote <$> listOf1 (elements $ alphabet ++ " ")+ shrink (Quoted q) = Quoted . quote <$> shrink contents+ where contents = init $ tail q++instance FromCell Quoted where+ fromCell = fmap (Quoted . quote) <$> fromCell
test/test-kvc.hs view
@@ -1,17 +1,17 @@-module Main where - -import qualified Test.Csv as Csv -import qualified Test.KeyValue as KeyValue - -import System.Exit - -main :: IO () -main = do - tests <- sequence - [ Csv.test - , KeyValue.test - ] - if all id tests - then exitSuccess - else exitFailure - +module Main where++import qualified Test.Csv as Csv+import qualified Test.KeyValue as KeyValue++import System.Exit++main :: IO ()+main = do+ tests <- sequence+ [ Csv.test+ , KeyValue.test+ ]+ if all id tests+ then exitSuccess+ else exitFailure+