{-# LANGUAGE TypeOperators #-} module Util.Csv ((:::)((:::)), decodeFile, decodeGZipFile, decodeFileStream, decodeGZipFileStream) where import Data.Csv ( HasHeader(..), FromRecord(..), FromField(..) , ToRecord(..), ToField(..), decode) import qualified Data.Csv.Streaming as CS (decode) import Codec.Compression.GZip (decompress) import qualified Data.Foldable as F import qualified Data.ByteString.Lazy as B import qualified Data.Vector as V import Control.Applicative ((<*>), (<$>)) data a ::: b = a ::: b deriving (Eq, Ord, Read, Show) infixr 5 ::: instance (FromField a, FromRecord b) => FromRecord (a ::: b) where parseRecord v | V.null v = fail "too few fields in input record" | otherwise = (:::) <$> parseField (V.head v) <*> parseRecord (V.tail v) instance (ToField a, ToRecord b) => ToRecord (a ::: b) where toRecord (a ::: b) = V.cons (toField a) (toRecord b) decodeBytes :: FromRecord a => B.ByteString -> [a] decodeBytes bs = case decode HasHeader bs of Left _ -> [] Right v -> V.toList v decodeFile :: FromRecord a => FilePath -> IO [a] decodeFile = fmap decodeBytes . B.readFile decodeGZipFile :: FromRecord a => FilePath -> IO [a] decodeGZipFile = fmap (decodeBytes . decompress) . B.readFile decodeFileStream :: FromRecord a => FilePath -> IO [a] decodeFileStream = fmap (F.toList . CS.decode HasHeader) . B.readFile decodeGZipFileStream :: FromRecord a => FilePath -> IO [a] decodeGZipFileStream = fmap (F.toList . CS.decode HasHeader . decompress) . B.readFile