packages feed

box-csv 0.0.3 → 0.2.0

raw patch · 3 files changed

Files

box-csv.cabal view
@@ -1,67 +1,37 @@ cabal-version: 2.4-name: box-csv-version: 0.0.3-synopsis: See readme.md-description: See readme.md for description.-category: project-author: Tony Day-maintainer: tonyday567@gmail.com-copyright: Tony Day (c) AfterTimes-license: BSD-3-Clause-homepage: https://github.com/tonyday567/box-csv#readme-bug-reports: https://github.com/tonyday567/box-csv/issues-build-type: Simple+name:          box-csv+version:       0.2.0+synopsis:      CSV parsing in a box.+description:   CSV parsing using attoparsec and the box library.+category:      project+author:        Tony Day+maintainer:    tonyday567@gmail.com+copyright:     Tony Day (c) 2018-2022+license:       BSD-3-Clause+homepage:      https://github.com/tonyday567/box-csv#readme+bug-reports:   https://github.com/tonyday567/box-csv/issues+build-type:    Simple+tested-with:   GHC ==8.8.4 || ==8.10.7 || ==9.2.1+ source-repository head-  type: git+  type:     git   location: https://github.com/tonyday567/box-csv  library-  hs-source-dirs:-    src+  hs-source-dirs:     src   default-extensions:-    NoImplicitPrelude-    NegativeLiterals-    OverloadedStrings-    UnicodeSyntax   ghc-options:-    -Wall-    -Wcompat-    -Wincomplete-record-updates-    -Wincomplete-uni-patterns-    -Wredundant-constraints-  build-depends:-    attoparsec >= 0.13,-    base >=4.7 && <5,-    box >= 0.6 && < 0.7,-    generic-lens,-    lens >= 4.19,-    numhask >= 0.6 && < 0.7,-    scientific >= 0.3,-    text >= 1.2,-    time >= 1.9,-  exposed-modules:-    Box.Csv-  other-modules:-  default-language: Haskell2010+    -Wall -Wcompat -Wincomplete-record-updates+    -Wincomplete-uni-patterns -Wredundant-constraints -fwrite-ide-info+    -hiedir=.hie -Wunused-packages -test-suite test-  type: exitcode-stdio-1.0-  main-is: test.hs-  hs-source-dirs:-      test-  default-extensions:-    NoImplicitPrelude-    NegativeLiterals-    OverloadedStrings-    UnicodeSyntax-  ghc-options:-    -Wall-    -Wcompat-    -Wincomplete-record-updates-    -Wincomplete-uni-patterns-    -Wredundant-constraints   build-depends:-    base >=4.7 && <5,-    doctest >= 0.16,-    numhask >= 0.6 && < 0.7-  default-language: Haskell2010+    , attoparsec  >=0.13 && <0.16+    , base        >=4.7  && <5+    , box         ^>=0.8+    , text        ^>=1.2+    , time        ^>=1.9++  exposed-modules:    Box.Csv+  other-modules:+  default-language:   Haskell2010
src/Box/Csv.hs view
@@ -1,16 +1,13 @@ {-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE OverloadedLabels #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE StrictData #-} {-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE NoImplicitPrelude #-}  -- | A csv process based on attoparsec and the box library--- module Box.Csv   ( CsvConfig (..),     defaultCsvConfig,-    file,     Header (..),     rowEmitter,     rowCommitter,@@ -27,7 +24,6 @@     A.double,     double',     fields,-    scis,     ints,     doubles,     day',@@ -37,81 +33,70 @@ where  import Box-import Control.Lens+import Control.Monad import qualified Data.Attoparsec.Text as A-import Data.Generics.Labels ()-import Data.Scientific+import Data.Functor.Contravariant+import Data.Text (Text, unpack) import qualified Data.Text as Text-import NumHask.Prelude import Data.Time+import GHC.Generics  -- $setup -- >>> :set -XOverloadedStrings+-- >>> import Box+-- >>> import Box.Csv+-- >>> import qualified Data.Text as Text+-- >>> import qualified Data.Attoparsec.Text as A  -- | csv file configuration-data CsvConfig-  = CsvConfig-      { -- | file name stem-        name :: Text,-        -- | file suffix-        suffix :: Text,-        -- | directory-        dir :: Text,-        -- | field separator-        fsep :: Char,-        -- | nature of header row(s)-        header :: Header-      }+data CsvConfig = CsvConfig+  { -- | file name+    file :: FilePath,+    -- | field separator+    fsep :: Char,+    -- | nature of header row(s)+    header :: Header+  }   deriving (Show, Generic, Eq)  -- | default csv file details -- -- >>> defaultCsvConfig--- CsvConfig {name = "time_series_covid19_deaths_global_narrow", suffix = ".csv", dir = "./other", fsep = ',', header = HasHXL}+-- CsvConfig {file = "./other/time_series_covid19_deaths_global_narrow.csv", fsep = ',', header = HasHXL} -- -- test data from https://data.humdata.org/dataset/novel-coronavirus-2019-ncov-cases defaultCsvConfig :: CsvConfig defaultCsvConfig =   CsvConfig-    "time_series_covid19_deaths_global_narrow"-    ".csv"-    "./other"+    "./other/time_series_covid19_deaths_global_narrow.csv"     ','     HasHXL --- | filepath for the config.------ >>> file defaultCsvConfig--- "./other/time_series_covid19_deaths_global_narrow.csv"-file :: CsvConfig -> FilePath-file cfg =-  cfg ^. #dir-    <> "/"-    <> cfg ^. #name-    <> cfg ^. #suffix-      & unpack- -- | Type of header rows.  Note the modern propensity for multiple header rows. data Header = HasHeader | HasHXL | NoHeader deriving (Show, Eq) +-- | attoparsec parse emitter which returns the original text on failure+parseE :: (Functor m) => A.Parser a -> Emitter m Text -> Emitter m (Either Text a)+parseE parser e = (\t -> either (const $ Left t) Right (A.parseOnly parser t)) <$> e+ -- | A continuation emitter of parsed csv rows from a CsvConfig, returning the original text on failure--- >>> rowEmitter defaultCsvConfig fields `with` emit+-- >>> emit <$|> rowEmitter defaultCsvConfig fields -- Just (Right ["Province/State","Country/Region","Lat","Long","Date","Value","ISO 3166-1 Alpha 3-Codes","Region Code","Sub-region Code","Intermediate Region Code\r"])-rowEmitter :: CsvConfig -> (Char -> A.Parser a) -> Cont IO (Emitter IO (Either Text a))-rowEmitter cfg p = parseE (p (view #fsep cfg)) <$> fileE (file cfg)+rowEmitter :: CsvConfig -> (Char -> A.Parser a) -> CoEmitter IO (Either Text a)+rowEmitter cfg p = parseE (p (fsep cfg)) <$> fileE (file cfg)  -- | commits printed csv rows ----- >>> let testConfig = CsvConfig "test" ".csv" "./test" ',' NoHeader--- >>> let ctest = rowCommitter testConfig (fmap (Text.intercalate "," . fmap show))--- >>> ctest `with` (\c -> commit c [[1..10::Int]])+-- >>> let testConfig = CsvConfig "./test/test.csv" ',' NoHeader+-- >>> let ctest = rowCommitter testConfig (fmap (Text.intercalate "," . fmap (Text.pack . show)))+--+-- >>> (\c -> commit c [[1..10::Int]]) <$|> ctest -- True ----- >>> rowEmitter testConfig ints `with` emit+-- >>> emit <$|> rowEmitter testConfig ints -- Just (Right [1,2,3,4,5,6,7,8,9,10])----rowCommitter :: CsvConfig -> (a -> [Text]) -> Cont IO (Committer IO a)-rowCommitter cfg f = contramap (Text.intercalate (Text.singleton $ view #fsep cfg) . f) <$> fileWriteC (file cfg)+rowCommitter :: CsvConfig -> (a -> [Text]) -> CoCommitter IO a+rowCommitter cfg f = contramap (Text.intercalate (Text.singleton $ fsep cfg) . f) <$> fileWriteC (file cfg)  -- | Run a parser across all lines of a file. --@@ -125,7 +110,7 @@ -- >>> take 2 $ drop 2 [x | (Right x) <- r1] -- [["","Afghanistan","33.0","65.0","2020-06-29","733","AFG","142","34","\r"],["","Afghanistan","33.0","65.0","2020-06-28","721","AFG","142","34","\r"]] runCsv :: CsvConfig -> (Char -> A.Parser a) -> IO [Either Text a]-runCsv cfg p = with (rowEmitter cfg p) toListE+runCsv cfg p = toListM <$|> rowEmitter cfg p  -- * low-level generic csv parser helpers @@ -223,22 +208,14 @@   pure d'  -- * Block list parsers+ -- | Parser for a csv row of [Text].--- TODO: deal with potential for an extra '\r' -- -- >>> A.parseOnly (fields ',') "field1,field2\r" -- Right ["field1","field2\r"]--- fields :: Char -> A.Parser [Text] fields c =   field_ c `A.sepBy1` sep c---- | parser for a csv row of [Scientific]------ >>> A.parseOnly (scis ',') "1,2.2,3.3"--- Right [1.0,2.2,3.3]-scis :: Char -> A.Parser [Scientific]-scis c = A.scientific `A.sepBy1` sep c  -- | parser for a csv row of [Double] --
− test/test.hs
@@ -1,19 +0,0 @@-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE OverloadedLabels #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE NoImplicitPrelude #-}-{-# OPTIONS_GHC -Wall #-}--module Main where--import NumHask.Prelude-import Test.DocTest--main :: IO ()-main =-  doctest-    [ "src/Box/Csv.hs"-    ]