conduit 0.0.1.1 → 0.0.2
raw patch · 13 files changed
+288/−7 lines, 13 filesbinary-addedPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Data.Conduit.Binary: dropWhile :: Resource m => (Word8 -> Bool) -> Sink ByteString m ()
+ Data.Conduit.Binary: head :: Resource m => Sink ByteString m (Maybe Word8)
+ Data.Conduit.Binary: openFile :: ResourceIO m => FilePath -> IOMode -> ResourceT m Handle
+ Data.Conduit.Binary: sinkHandle :: ResourceIO m => Handle -> Sink ByteString m ()
+ Data.Conduit.Binary: sourceHandle :: ResourceIO m => Handle -> Source m ByteString
+ Data.Conduit.Binary: takeWhile :: Resource m => (Word8 -> Bool) -> Conduit ByteString m ByteString
+ Data.Conduit.List: groupBy :: Resource m => (a -> a -> Bool) -> Conduit a m [a]
Files
- Data/Conduit/Binary.hs +109/−5
- Data/Conduit/Lazy.hs +3/−0
- Data/Conduit/List.hs +50/−0
- Data/Conduit/Text.hs +22/−0
- Data/Conduit/Types/Conduit.hs +6/−0
- Data/Conduit/Types/Sink.hs +6/−0
- Data/Conduit/Types/Source.hs +16/−0
- Data/Conduit/Util/Conduit.hs +12/−0
- Data/Conduit/Util/Sink.hs +6/−0
- Data/Conduit/Util/Source.hs +6/−0
- conduit.cabal +2/−2
- test/main.hs +50/−0
- test/random binary
Data/Conduit/Binary.hs view
@@ -3,25 +3,50 @@ -- | Functions for interacting with bytes. module Data.Conduit.Binary ( sourceFile + , sourceHandle , sourceFileRange , sinkFile + , sinkHandle , conduitFile , isolate + , openFile + , head + , takeWhile + , dropWhile ) where +import Prelude hiding (head, takeWhile, dropWhile) import qualified Data.ByteString as S import Data.Conduit import Control.Exception (assert) import Control.Monad.IO.Class (liftIO) import qualified System.IO as IO -import Control.Monad.Trans.Resource (withIO, release, newRef, readRef, writeRef) +import Control.Monad.Trans.Resource + ( withIO, release, newRef, readRef, writeRef + ) +import Data.Word (Word8) #if CABAL_OS_WINDOWS import qualified System.Win32File as F #elif NO_HANDLES import qualified System.PosixFile as F #endif +-- | Open a file 'IO.Handle' safely by automatically registering a release +-- action. +-- +-- While you are not required to call @hClose@ on the resulting handle, you +-- should do so as early as possible to free scarce resources. +-- +-- Since 0.0.2 +openFile :: ResourceIO m + => FilePath + -> IO.IOMode + -> ResourceT m IO.Handle +openFile fp mode = fmap snd $ withIO (IO.openBinaryFile fp mode) IO.hClose + -- | Stream the contents of a file as binary data. +-- +-- Since 0.0.0 sourceFile :: ResourceIO m => FilePath -> Source m S.ByteString @@ -31,7 +56,7 @@ F.close (liftIO . F.read) #else - (IO.openFile fp IO.ReadMode) + (IO.openBinaryFile fp IO.ReadMode) IO.hClose (\handle -> do bs <- liftIO $ S.hGetSome handle 4096 @@ -40,15 +65,46 @@ else return $ Open bs) #endif +-- | Stream the contents of a 'IO.Handle' as binary data. Note that this +-- function will /not/ automatically close the @Handle@ when processing +-- completes, since it did not acquire the @Handle@ in the first place. +-- +-- Since 0.0.2. +sourceHandle :: ResourceIO m + => IO.Handle + -> Source m S.ByteString +sourceHandle h = Source $ return $ PreparedSource + { sourcePull = do + bs <- liftIO (S.hGetSome h 4096) + if S.null bs + then return Closed + else return (Open bs) + , sourceClose = return () + } + +-- | Stream all incoming data to the given 'IO.Handle'. Note that this function +-- will /not/ automatically close the @Handle@ when processing completes. +-- +-- Since 0.0.2. +sinkHandle :: ResourceIO m + => IO.Handle + -> Sink S.ByteString m () +sinkHandle h = Sink $ return $ SinkData + { sinkPush = \input -> liftIO (S.hPut h input) >> return Processing + , sinkClose = return () + } + -- | Stream the contents of a file as binary data, starting from a certain -- offset and only consuming up to a certain number of bytes. +-- +-- Since 0.0.0 sourceFileRange :: ResourceIO m => FilePath -> Maybe Integer -- ^ Offset -> Maybe Integer -- ^ Maximum count -> Source m S.ByteString sourceFileRange fp offset count = Source $ do - (key, handle) <- withIO (IO.openFile fp IO.ReadMode) IO.hClose + (key, handle) <- withIO (IO.openBinaryFile fp IO.ReadMode) IO.hClose case offset of Nothing -> return () Just off -> liftIO $ IO.hSeek handle IO.AbsoluteSeek off @@ -84,22 +140,26 @@ return $ Open bs -- | Stream all incoming data to the given file. +-- +-- Since 0.0.0 sinkFile :: ResourceIO m => FilePath -> Sink S.ByteString m () sinkFile fp = sinkIO - (IO.openFile fp IO.WriteMode) + (IO.openBinaryFile fp IO.WriteMode) IO.hClose (\handle bs -> liftIO (S.hPut handle bs) >> return Processing) (const $ return ()) -- | Stream the contents of the input to a file, and also send it along the -- pipeline. Similar in concept to the Unix command @tee@. +-- +-- Since 0.0.0 conduitFile :: ResourceIO m => FilePath -> Conduit S.ByteString m S.ByteString conduitFile fp = conduitIO - (IO.openFile fp IO.WriteMode) + (IO.openBinaryFile fp IO.WriteMode) IO.hClose (\handle bs -> do liftIO $ S.hPut handle bs @@ -109,6 +169,8 @@ -- | Ensure that only up to the given number of bytes are consume by the inner -- sink. Note that this does /not/ ensure that all of those bytes are in fact -- consumed. +-- +-- Since 0.0.0 isolate :: Resource m => Int -> Conduit S.ByteString m S.ByteString @@ -126,3 +188,45 @@ then Finished (if S.null b then Nothing else Just b) (if S.null a then [] else [a]) else assert (S.null b) $ Producing [a]) close _ = return [] + +-- | Return the next byte from the stream, if available. +-- +-- Since 0.0.2 +head :: Resource m => Sink S.ByteString m (Maybe Word8) +head = Sink $ return $ SinkData + { sinkPush = \bs -> + case S.uncons bs of + Nothing -> return Processing + Just (w, bs') -> do + let lo = if S.null bs' then Nothing else Just bs' + return $ Done lo (Just w) + , sinkClose = return Nothing + } + +-- | Return all bytes while the predicate returns @True@. +-- +-- Since 0.0.2 +takeWhile :: Resource m => (Word8 -> Bool) -> Conduit S.ByteString m S.ByteString +takeWhile p = Conduit $ return $ PreparedConduit + { conduitPush = \bs -> do + let (x, y) = S.span p bs + return $ + if S.null y + then Producing [x] + else Finished (Just y) (if S.null x then [] else [x]) + , conduitClose = return [] + } + +-- | Ignore all bytes while the predicate returns @True@. +-- +-- Since 0.0.2 +dropWhile :: Resource m => (Word8 -> Bool) -> Sink S.ByteString m () +dropWhile p = Sink $ return $ SinkData + { sinkPush = \bs -> do + let bs' = S.dropWhile p bs + return $ + if S.null bs' + then Processing + else Done (Just bs') () + , sinkClose = return () + }
Data/Conduit/Lazy.hs view
@@ -10,6 +10,9 @@ import System.IO.Unsafe (unsafeInterleaveIO) import Control.Monad.Trans.Control +-- | Use lazy I\/O to consume all elements from a @Source@. +-- +-- Since 0.0.0 lazyConsume :: MonadBaseControl IO m => Source m a -> ResourceT m [a] lazyConsume (Source msrc) = do src <- msrc
Data/Conduit/List.hs view
@@ -27,6 +27,7 @@ -- ** Pure , map , concatMap + , groupBy , isolate , filter -- ** Monadic @@ -45,6 +46,8 @@ import Control.Monad.Trans.Class (lift) -- | A strict left fold. +-- +-- Since 0.0.0 fold :: Resource m => (b -> a -> b) -> b @@ -55,6 +58,8 @@ return -- | A monadic strict left fold. +-- +-- Since 0.0.0 foldM :: Resource m => (b -> a -> m b) -> b @@ -68,6 +73,8 @@ return -- | Apply the action to all values in the stream. +-- +-- Since 0.0.0 mapM_ :: Resource m => (a -> m ()) -> Sink a m () @@ -76,6 +83,8 @@ (return ()) -- | Convert a list into a source. +-- +-- Since 0.0.0 sourceList :: Resource m => [a] -> Source m a sourceList l0 = sourceState l0 go @@ -90,6 +99,8 @@ -- -- However, @drop@ is more efficient as it does not need to hold values in -- memory. +-- +-- Since 0.0.0 drop :: Resource m => Int -> Sink a m () @@ -111,6 +122,8 @@ -- This function is semantically equivalent to: -- -- > take i = isolate i =$ consume +-- +-- Since 0.0.0 take :: Resource m => Int -> Sink a m [a] @@ -130,6 +143,8 @@ close (_, front) = return $ front [] -- | Take a single value from the stream, if available. +-- +-- Since 0.0.0 head :: Resource m => Sink a m (Maybe a) head = Sink $ return $ SinkData push close @@ -139,6 +154,8 @@ -- | Look at the next value in the stream, if available. This function will not -- change the state of the stream. +-- +-- Since 0.0.0 peek :: Resource m => Sink a m (Maybe a) peek = Sink $ return $ SinkData push close @@ -147,6 +164,8 @@ close = return Nothing -- | Apply a transformation to all values in a stream. +-- +-- Since 0.0.0 map :: Monad m => (a -> b) -> Conduit a m b map f = Conduit $ return $ PreparedConduit { conduitPush = return . Producing . return . f @@ -157,6 +176,8 @@ -- -- If you do not need the transformed values, and instead just want the monadic -- side-effects of running the action, see 'mapM_'. +-- +-- Since 0.0.0 mapM :: Monad m => (a -> m b) -> Conduit a m b mapM f = Conduit $ return $ PreparedConduit { conduitPush = fmap (Producing . return) . lift . f @@ -165,6 +186,8 @@ -- | Apply a transformation to all values in a stream, concatenating the output -- values. +-- +-- Since 0.0.0 concatMap :: Monad m => (a -> [b]) -> Conduit a m b concatMap f = Conduit $ return $ PreparedConduit { conduitPush = return . Producing . f @@ -173,6 +196,8 @@ -- | Apply a monadic transformation to all values in a stream, concatenating -- the output values. +-- +-- Since 0.0.0 concatMapM :: Monad m => (a -> m [b]) -> Conduit a m b concatMapM f = Conduit $ return $ PreparedConduit { conduitPush = fmap Producing . lift . f @@ -182,12 +207,31 @@ -- | Consume all values from the stream and return as a list. Note that this -- will pull all values into memory. For a lazy variant, see -- "Data.Conduit.Lazy". +-- +-- Since 0.0.0 consume :: Resource m => Sink a m [a] consume = sinkState id (\front input -> return (front . (input :), Processing)) (\front -> return $ front []) +-- | Grouping input according to an equality function. +-- +-- Since 0.0.2 +groupBy :: Resource m => (a -> a -> Bool) -> Conduit a m [a] +groupBy f = conduitState + [] + push + close + where + push [] v = return ([v], Producing []) + push s@(x:_) v = + if f x v then + return (v:s, Producing []) + else + return ([v], Producing [s]) + close s = return [s] + -- | Ensure that the inner sink consumes no more than the given number of -- values. Note this this does /not/ ensure that the sink consumes all of those -- values. To get the latter behavior, combine with 'sinkNull', e.g.: @@ -199,6 +243,8 @@ -- > return x -- > someOtherSink -- > ... +-- +-- Since 0.0.0 isolate :: Resource m => Int -> Conduit a m a isolate count0 = conduitState count0 @@ -217,6 +263,8 @@ else Producing [x]) -- | Keep only values in the stream passing a given predicate. +-- +-- Since 0.0.0 filter :: Resource m => (a -> Bool) -> Conduit a m a filter f = Conduit $ return $ PreparedConduit { conduitPush = return . Producing . Prelude.filter f . return @@ -225,6 +273,8 @@ -- | Ignore the remainder of values in the source. Particularly useful when -- combined with 'isolate'. +-- +-- Since 0.0.0 sinkNull :: Resource m => Sink a m () sinkNull = Sink $ return $ SinkData (\_ -> return Processing)
Data/Conduit/Text.hs view
@@ -46,6 +46,8 @@ import Control.Monad.Trans.Resource (ResourceThrow (..)) -- | A specific character encoding. +-- +-- Since 0.0.0 data Codec = Codec { codecName :: T.Text , codecEncode @@ -64,6 +66,8 @@ -- | Convert text into bytes, using the provided codec. If the codec is -- not capable of representing an input character, an exception will be thrown. +-- +-- Since 0.0.0 encode :: ResourceThrow m => Codec -> C.Conduit T.Text m B.ByteString encode codec = CL.mapM $ \t -> do let (bs, mexc) = codecEncode codec t @@ -72,6 +76,8 @@ -- | Convert bytes into text, using the provided codec. If the codec is -- not capable of decoding an input byte sequence, an exception will be thrown. +-- +-- Since 0.0.0 decode :: ResourceThrow m => Codec -> C.Conduit B.ByteString m T.Text decode codec = C.conduitState Nothing @@ -108,6 +114,8 @@ (text, extra) = codecDecode codec x front' = front . (text:) +-- | +-- Since 0.0.0 data TextException = DecodeException Codec Word8 | EncodeException Codec Char deriving (Show, Typeable) @@ -139,6 +147,8 @@ -- is only called when parsing failed somewhere Right _ -> Right B.empty) +-- | +-- Since 0.0.0 utf8 :: Codec utf8 = Codec name enc dec where name = T.pack "UTF-8" @@ -170,6 +180,8 @@ then Just tooLong else decodeMore +-- | +-- Since 0.0.0 utf16_le :: Codec utf16_le = Codec name enc dec where name = T.pack "UTF-16-LE" @@ -195,6 +207,8 @@ decodeTo n = first TE.decodeUtf16LE (B.splitAt n bytes) decodeAll = (TE.decodeUtf16LE bytes, B.empty) +-- | +-- Since 0.0.0 utf16_be :: Codec utf16_be = Codec name enc dec where name = T.pack "UTF-16-BE" @@ -228,6 +242,8 @@ x :: Word16 x = (fromIntegral x1 `shiftL` 8) .|. fromIntegral x0 +-- | +-- Since 0.0.0 utf32_le :: Codec utf32_le = Codec name enc dec where name = T.pack "UTF-32-LE" @@ -236,6 +252,8 @@ Just (text, extra) -> (text, Right extra) Nothing -> splitSlowly TE.decodeUtf32LE bs +-- | +-- Since 0.0.0 utf32_be :: Codec utf32_be = Codec name enc dec where name = T.pack "UTF-32-BE" @@ -257,6 +275,8 @@ then (bytes, B.empty) else B.splitAt lenToDecode bytes +-- | +-- Since 0.0.0 ascii :: Codec ascii = Codec name enc dec where name = T.pack "ASCII" @@ -274,6 +294,8 @@ then Right B.empty else Left (DecodeException ascii (B.head unsafe), unsafe) +-- | +-- Since 0.0.0 iso8859_1 :: Codec iso8859_1 = Codec name enc dec where name = T.pack "ISO-8859-1"
Data/Conduit/Types/Conduit.hs view
@@ -13,6 +13,8 @@ -- still producing output and provide some, or indicate that it is finished -- producing output, in which case it returns optional leftover input and some -- final output. +-- +-- Since 0.0.0 data ConduitResult input output = Producing [output] | Finished (Maybe input) [output] instance Functor (ConduitResult input) where @@ -26,6 +28,8 @@ -- -- * Neither a push nor close may be performed after a conduit returns a -- 'Finished' from a push, or after a close is performed. +-- +-- Since 0.0.0 data PreparedConduit input m output = PreparedConduit { conduitPush :: input -> ResourceT m (ConduitResult input output) , conduitClose :: ResourceT m [output] @@ -39,6 +43,8 @@ -- | A monadic action generating a 'PreparedConduit'. See @Source@ and @Sink@ -- for more motivation. +-- +-- Since 0.0.0 newtype Conduit input m output = Conduit { prepareConduit :: ResourceT m (PreparedConduit input m output) }
Data/Conduit/Types/Sink.hs view
@@ -21,6 +21,8 @@ -- pushed to it, a @Sink@ may indicate that it is still processing data, or -- that it is done, in which case it returns some optional leftover input and -- an output value. +-- +-- Since 0.0.0 data SinkResult input output = Processing | Done (Maybe input) output instance Functor (SinkResult input) where fmap _ Processing = Processing @@ -52,6 +54,8 @@ -- it must do so whenever it returns a result, either via @sinkPush@ or -- @sinkClose@. Note that, due to usage of @ResourceT@, this is merely an -- optimization. +-- +-- Since 0.0.0 data PreparedSink input m output = SinkNoData output | SinkData @@ -73,6 +77,8 @@ -- -- Note that this type provides a 'Monad' instance, allowing you to easily -- compose @Sink@s together. +-- +-- Since 0.0.0 newtype Sink input m output = Sink { prepareSink :: ResourceT m (PreparedSink input m output) } instance Monad m => Functor (Sink input m) where
Data/Conduit/Types/Source.hs view
@@ -19,6 +19,8 @@ -- | Result of pulling from a source. Either a new piece of data (@Open@), or -- indicates that the source is now @Closed@. +-- +-- Since 0.0.0 data SourceResult a = Open a | Closed deriving (Show, Eq, Ord) @@ -42,6 +44,8 @@ -- * A 'PreparedSource' is responsible to free any resources when either 'sourceClose' -- is called or a 'Closed' is returned. However, based on the usage of -- 'ResourceT', this is simply an optimization. +-- +-- Since 0.0.0 data PreparedSource m a = PreparedSource { sourcePull :: ResourceT m (SourceResult a) , sourceClose :: ResourceT m () @@ -66,6 +70,8 @@ -- Note that each time you \"call\" a @Source@, it is started from scratch. If -- you want a resumable source (e.g., one which can be passed to multiple -- @Sink@s), you likely want to use a 'BufferedSource'. +-- +-- Since 0.0.0 newtype Source m a = Source { prepareSource :: ResourceT m (PreparedSource m a) } instance Monad m => Functor (Source m) where @@ -133,17 +139,23 @@ -- while the types will allow you to use the buffered source in multiple -- threads, there is no guarantee that all @BufferedSource@s will handle this -- correctly. +-- +-- Since 0.0.0 data BufferedSource m a = BufferedSource { bsourcePull :: ResourceT m (SourceResult a) , bsourceUnpull :: a -> ResourceT m () , bsourceClose :: ResourceT m () } +-- | +-- Since 0.0.0 data SourceInvariantException = PullAfterEOF String deriving (Show, Typeable) instance Exception SourceInvariantException -- | This typeclass allows us to unify operators on 'Source' and 'BufferedSource'. +-- +-- Since 0.0.0 class BufferSource s where bufferSource :: Resource m => s m a -> ResourceT m (BufferedSource m a) @@ -211,6 +223,10 @@ -- mean your original 'BufferedSource' will be closed. Additionally, all -- leftover data from usage of the returned @Source@ will be discarded. In -- other words: this is a no-going-back move. +-- +-- Note: @bufferSource@ . @unbufferSource@ is /not/ the identity function. +-- +-- Since 0.0.1 unbufferSource :: Monad m => BufferedSource m a -> Source m a
Data/Conduit/Util/Conduit.hs view
@@ -22,6 +22,8 @@ -- | Construct a 'Conduit' with some stateful functions. This function address -- all mutable state for you. +-- +-- Since 0.0.0 conduitState :: Resource m => state -- ^ initial state @@ -56,6 +58,8 @@ } -- | Construct a 'Conduit'. +-- +-- Since 0.0.0 conduitIO :: ResourceIO m => IO state -- ^ resource and/or state allocation -> (state -> IO ()) -- ^ resource and/or state cleanup @@ -92,6 +96,8 @@ } -- | Transform the monad a 'Conduit' lives in. +-- +-- Since 0.0.0 transConduit :: (Monad m, Base m ~ Base n) => (forall a. m a -> n a) -> Conduit input m output @@ -105,6 +111,8 @@ } -- | Return value from a 'SequencedSink'. +-- +-- Since 0.0.0 data SequencedSinkResponse state input m output = Emit state [output] -- ^ Set a new state, and emit some new output. | Stop -- ^ End the conduit. @@ -113,6 +121,8 @@ -- | Helper type for constructing a @Conduit@ based on @Sink@s. This allows you -- to write higher-level code that takes advantage of existing conduits and -- sinks, and leverages a sink's monadic interface. +-- +-- Since 0.0.0 type SequencedSink state input m output = state -> Sink input m (SequencedSinkResponse state input m output) @@ -123,6 +133,8 @@ (ResourceT m (SequencedSinkResponse state input m output)) -- | Convert a 'SequencedSink' into a 'Conduit'. +-- +-- Since 0.0.0 sequenceSink :: Resource m => state -- ^ initial state
Data/Conduit/Util/Sink.hs view
@@ -17,6 +17,8 @@ -- | Construct a 'Sink' with some stateful functions. This function address -- all mutable state for you. +-- +-- Since 0.0.0 sinkState :: Resource m => state -- ^ initial state @@ -52,6 +54,8 @@ -- | Construct a 'Sink'. Note that your push and close functions need not -- explicitly perform any cleanup. +-- +-- Since 0.0.0 sinkIO :: ResourceIO m => IO state -- ^ resource and/or state allocation -> (state -> IO ()) -- ^ resource and/or state cleanup @@ -88,6 +92,8 @@ } -- | Transform the monad a 'Sink' lives in. +-- +-- Since 0.0.0 transSink :: (Base m ~ Base n, Monad m) => (forall a. m a -> n a) -> Sink input m output
Data/Conduit/Util/Source.hs view
@@ -18,6 +18,8 @@ -- | Construct a 'Source' with some stateful functions. This function address -- all mutable state for you. +-- +-- Since 0.0.0 sourceState :: Resource m => state -- ^ Initial state @@ -54,6 +56,8 @@ } -- | Construct a 'Source' based on some IO actions for alloc/release. +-- +-- Since 0.0.0 sourceIO :: ResourceIO m => IO state -- ^ resource and/or state allocation -> (state -> IO ()) -- ^ resource and/or state cleanup @@ -87,6 +91,8 @@ } -- | Transform the monad a 'Source' lives in. +-- +-- Since 0.0.0 transSource :: (Base m ~ Base n, Monad m) => (forall a. m a -> n a) -> Source m output
conduit.cabal view
@@ -1,5 +1,5 @@ Name: conduit -Version: 0.0.1.1 +Version: 0.0.2 Synopsis: A pull-based approach to streaming data. Description: Conduits are an approach to the streaming data problem. It is meant as an alternative to enumerators\/iterators, hoping to address the same issues with different trade-offs based on real-world experience with enumerators. For more information, see <http://www.yesodweb.com/blog/2011/12/conduits>. License: BSD3 @@ -10,7 +10,7 @@ Build-type: Simple Cabal-version: >=1.8 Homepage: http://github.com/snoyberg/conduit -extra-source-files: test/main.hs +extra-source-files: test/main.hs, test/random flag debug
test/main.hs view
@@ -11,6 +11,7 @@ import qualified Data.Conduit.Binary as CB import qualified Data.Conduit.Text as CT import Data.Conduit (runResourceT) +import qualified Data.List as DL import Control.Monad.ST (runST) import Data.Monoid import qualified Data.ByteString as S @@ -159,6 +160,20 @@ C.=$ CL.fold (+) 0 x @?= 2 * sum [1..10 :: Int] + it "groupBy" $ do + let input = [1::Int, 1, 2, 3, 3, 3, 4, 5, 5] + x <- runResourceT $ CL.sourceList input + C.$$ CL.groupBy (==) + C.=$ CL.consume + x @?= DL.groupBy (==) input + + it "groupBy (nondup begin/end)" $ do + let input = [1::Int, 2, 3, 3, 3, 4, 5] + x <- runResourceT $ CL.sourceList input + C.$$ CL.groupBy (==) + C.=$ CL.consume + x @?= DL.groupBy (==) input + it "concatMap" $ do let input = [1, 11, 21] x <- runResourceT $ CL.sourceList input @@ -304,6 +319,41 @@ let src = C.unbufferSource bsrc src C.$$ CL.fold (+) 0 x @?= sum [6..10] + + describe "properly using binary file reading" $ do + it "sourceFile" $ do + x <- runResourceT $ CB.sourceFile "test/random" C.$$ CL.consume + lbs <- L.readFile "test/random" + L.fromChunks x @?= lbs + + describe "binary head" $ do + let go lbs = do + x <- CB.head + case (x, L.uncons lbs) of + (Nothing, Nothing) -> return True + (Just y, Just (z, lbs')) + | y == z -> go lbs' + _ -> return False + + prop "works" $ \bss' -> + let bss = map S.pack bss' + in runST $ runResourceT $ + CL.sourceList bss C.$$ go (L.fromChunks bss) + describe "binary takeWhile" $ do + prop "works" $ \bss' -> + let bss = map S.pack bss' + in runST $ runResourceT $ do + bss2 <- CL.sourceList bss C.$$ CB.takeWhile (>= 5) C.=$ CL.consume + return $ L.fromChunks bss2 == L.takeWhile (>= 5) (L.fromChunks bss) + + describe "binary dropWhile" $ do + prop "works" $ \bss' -> + let bss = map S.pack bss' + in runST $ runResourceT $ do + bss2 <- CL.sourceList bss C.$$ do + CB.dropWhile (< 5) + CL.consume + return $ L.fromChunks bss2 == L.dropWhile (< 5) (L.fromChunks bss) it' :: String -> IO () -> Writer [Spec] () it' = it
+ test/random view
binary file changed (absent → 1024 bytes)