module Data.Csv.Streaming
(
Records(..)
, decode
, decodeWith
, decodeByName
, decodeByNameWith
) where
import Control.Applicative ((<$>), (<*>), pure)
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as BL
import qualified Data.ByteString.Lazy.Char8 as BL8
import Data.Foldable (Foldable(..))
import Data.Traversable (Traversable(..))
import Prelude hiding (foldr)
import Data.Csv.Conversion
import Data.Csv.Incremental hiding (decode, decodeByName, decodeByNameWith,
decodeWith)
import qualified Data.Csv.Incremental as I
import Data.Csv.Parser
import Data.Csv.Types
data Records a
=
Cons (Either String a) (Records a)
| Nil (Maybe String) BL.ByteString
deriving (Eq, Functor, Show)
instance Foldable Records where
foldr = foldrRecords
#if MIN_VERSION_base(4,6,0)
foldl' = foldlRecords'
#endif
foldrRecords :: (a -> b -> b) -> b -> Records a -> b
foldrRecords f = go
where
go z (Cons (Right x) rs) = f x (go z rs)
go z _ = z
#if MIN_VERSION_base(4,6,0)
foldlRecords' :: (a -> b -> a) -> a -> Records b -> a
foldlRecords' f = go
where
go z (Cons (Right x) rs) = let z' = f z x in z' `seq` go z' rs
go z _ = z
#endif
instance Traversable Records where
traverse _ (Nil merr rest) = pure $ Nil merr rest
traverse f (Cons x xs) = Cons <$> traverseElem x <*> traverse f xs
where
traverseElem (Left err) = pure $ Left err
traverseElem (Right y) = Right <$> f y
decode :: FromRecord a
=> Bool
-> BL.ByteString
-> Records a
decode = decodeWith defaultDecodeOptions
decodeWith :: FromRecord a
=> DecodeOptions
-> Bool
-> BL.ByteString
-> Records a
decodeWith !opts skipHeader s0 = case BL.toChunks s0 of
[] -> go [] (feedEndOfInput $ I.decodeWith opts skipHeader)
(s:ss) -> go ss (I.decodeWith opts skipHeader `feedChunk` s)
where
go ss (Done xs) = foldr Cons (Nil Nothing (BL.fromChunks ss)) xs
go ss (Fail rest err) = Nil (Just err) (BL.fromChunks (rest:ss))
go [] (Partial k) = go [] (k B.empty)
go (s:ss) (Partial k) = go ss (k s)
go [] (Some xs k) = foldr Cons (go [] (k B.empty)) xs
go (s:ss) (Some xs k) = foldr Cons (go ss (k s)) xs
decodeByName :: FromNamedRecord a
=> BL.ByteString
-> Either String (Header, Records a)
decodeByName = decodeByNameWith defaultDecodeOptions
decodeByNameWith :: FromNamedRecord a
=> DecodeOptions
-> BL.ByteString
-> Either String (Header, Records a)
decodeByNameWith !opts s0 = case BL.toChunks s0 of
[] -> go [] (feedEndOfInputH $ I.decodeByNameWith opts)
(s:ss) -> go ss (I.decodeByNameWith opts `feedChunkH` s)
where
go ss (DoneH hdr p) = Right (hdr, go2 ss p)
go ss (FailH rest err) = Left $ err ++ " at " ++
show (BL8.unpack . BL.fromChunks $ rest : ss)
go [] (PartialH k) = go [] (k B.empty)
go (s:ss) (PartialH k) = go ss (k s)
go2 ss (Done xs) = foldr Cons (Nil Nothing (BL.fromChunks ss)) xs
go2 ss (Fail rest err) = Nil (Just err) (BL.fromChunks (rest:ss))
go2 [] (Partial k) = go2 [] (k B.empty)
go2 (s:ss) (Partial k) = go2 ss (k s)
go2 [] (Some xs k) = foldr Cons (go2 [] (k B.empty)) xs
go2 (s:ss) (Some xs k) = foldr Cons (go2 ss (k s)) xs