conduit 1.2.9 → 1.2.9.1
raw patch · 4 files changed
+59/−13 lines, 4 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- ChangeLog.md +6/−0
- Data/Conduit/Internal/Conduit.hs +43/−12
- conduit.cabal +1/−1
- test/main.hs +9/−0
ChangeLog.md view
@@ -1,3 +1,9 @@+## 1.2.9.1++* Ensure downstream and inner sink receive same inputs in+ `passthroughSink`+ [#304](https://github.com/snoyberg/conduit/issues/304)+ ## 1.2.9 * `chunksOf` [#296](https://github.com/snoyberg/conduit/pull/296)
Data/Conduit/Internal/Conduit.hs view
@@ -703,23 +703,54 @@ -> (r -> m ()) -- ^ finalizer -> Conduit i m i passthroughSink (ConduitM sink0) final = ConduitM $ \rest -> let- go _ (Done r) = do+ -- A bit of explanation is in order, this function is+ -- non-obvious. The purpose of go is to keep track of the sink+ -- we're passing values to, and then yield values downstream. The+ -- third argument to go is the current state of that sink. That's+ -- relatively straightforward.+ --+ -- The second value is the leftover buffer. These are values that+ -- the sink itself has called leftover on, and must be provided+ -- back to the sink the next time it awaits. _However_, these+ -- values should _not_ be reyielded downstream: we have already+ -- yielded them downstream ourself, and it is the responsibility+ -- of the functions wrapping around passthroughSink to handle the+ -- leftovers from downstream.+ --+ -- The trickiest bit is the first argument, which is a solution to+ -- bug https://github.com/snoyberg/conduit/issues/304. The issue+ -- is that, once we get a value, we need to provide it to both the+ -- inner sink _and_ yield it downstream. The obvious thing to do+ -- is yield first and then recursively call go. Unfortunately,+ -- this doesn't work in all cases: if the downstream component+ -- never calls await again, our yield call will never return, and+ -- our sink will not get the last value. This results is confusing+ -- behavior where the sink and downstream component receive a+ -- different number of values.+ --+ -- Solution: keep a buffer of the next value to yield downstream,+ -- and only yield it downstream in one of two cases: our sink is+ -- asking for another value, or our sink is done. This way, we+ -- ensure that, in all cases, we pass exactly the same number of+ -- values to the inner sink as to downstream.++ go mbuf _ (Done r) = do+ maybe (return ()) CI.yield mbuf lift $ final r unConduitM (awaitForever yield) rest- go is (Leftover sink i) = go (i:is) sink- go _ (HaveOutput _ _ o) = absurd o- go is (PipeM mx) = do+ go mbuf is (Leftover sink i) = go mbuf (i:is) sink+ go _ _ (HaveOutput _ _ o) = absurd o+ go mbuf is (PipeM mx) = do x <- lift mx- go is x- go (i:is) (NeedInput next _) = go is (next i)- go [] (NeedInput next done) = do+ go mbuf is x+ go mbuf (i:is) (NeedInput next _) = go mbuf is (next i)+ go mbuf [] (NeedInput next done) = do+ maybe (return ()) CI.yield mbuf mx <- CI.await case mx of- Nothing -> go [] (done ())- Just x -> do- CI.yield x- go [] (next x)- in go [] (sink0 Done)+ Nothing -> go Nothing [] (done ())+ Just x -> go (Just x) [] (next x)+ in go Nothing [] (sink0 Done) -- | Convert a @Source@ into a list. The basic functionality can be explained as: --
conduit.cabal view
@@ -1,5 +1,5 @@ Name: conduit-Version: 1.2.9+Version: 1.2.9.1 Synopsis: Streaming data processing library. description: `conduit` is a solution to the streaming data problem, allowing for production,
test/main.hs view
@@ -854,6 +854,15 @@ x <- I.readIORef ref x `shouldBe` (-1) + it "handles the last input correctly #304" $ do+ ref <- I.newIORef (-1 :: Int)+ let sink = CL.mapM_ (I.writeIORef ref)+ conduit = C.passthroughSink sink (const (return ()))+ res <- mapM_ C.yield [1..] C.$$ conduit C.=$ CL.take 5+ res `shouldBe` [1..5]+ x <- I.readIORef ref+ x `shouldBe` 5+ describe "mtl instances" $ do it "ErrorT" $ do let src = flip catchError (const $ C.yield 4) $ do