@@ -20,6 +20,7 @@ module Streamly.Internal.Data.Stream.StreamD.Exception
2020 , finally
2121 , ghandle
2222 , handle
23+ , retry
2324 )
2425where
2526
@@ -29,11 +30,13 @@ import Control.Exception (Exception, SomeException, mask_)
2930import Control.Monad.Catch (MonadCatch)
3031import Control.Monad.IO.Class (MonadIO(..))
3132import Control.Monad.Trans.Control (MonadBaseControl, liftBaseOp_)
33+import Data.Map.Strict (Map)
3234import GHC.Exts (inline)
3335import Streamly.Internal.Data.IOFinalizer
3436 (newIOFinalizer, runIOFinalizer, clearingIOFinalizer)
3537
3638import qualified Control.Monad.Catch as MC
39+import qualified Data.Map.Strict as Map
3740
3841import Streamly.Internal.Data.Stream.StreamD.Type
3942import Streamly.Internal.Data.SVar
@@ -357,3 +360,55 @@ _handle f (Stream step state) = Stream step' (Left state)
357360 Yield x s -> return $ Yield x (Right (Stream step1 s))
358361 Skip s -> return $ Skip (Right (Stream step1 s))
359362 Stop -> return Stop
363+
364+data RetryState emap s1 s2
365+ = RetryWithMap emap s1
366+ | RetryDefault s2
367+
368+-- | See 'Streamly.Internal.Data.Stream.IsStream.retry'
369+--
370+{-# INLINE_NORMAL retry #-}
371+retry
372+ :: forall e m a. (Exception e, Ord e, MonadCatch m)
373+ => Map e Int
374+ -- ^ map from exception to retry count
375+ -> (e -> Stream m a)
376+ -- ^ default handler for those exceptions that are not in the map
377+ -> Stream m a
378+ -> Stream m a
379+retry emap0 defaultHandler (Stream step0 state0) = Stream step state
380+
381+ where
382+
383+ state = RetryWithMap emap0 state0
384+
385+ {-# INLINE_LATE step #-}
386+ step gst (RetryWithMap emap st) = do
387+ eres <- MC.try $ step0 gst st
388+ case eres of
389+ Left e -> handler e emap st
390+ Right res ->
391+ return
392+ $ case res of
393+ Yield x st1 -> Yield x $ RetryWithMap emap st1
394+ Skip st1 -> Skip $ RetryWithMap emap st1
395+ Stop -> Stop
396+ step gst (RetryDefault (UnStream step1 state1)) = do
397+ res <- step1 gst state1
398+ return
399+ $ case res of
400+ Yield x st1 -> Yield x $ RetryDefault (Stream step1 st1)
401+ Skip st1 -> Skip $ RetryDefault (Stream step1 st1)
402+ Stop -> Stop
403+
404+ {-# INLINE handler #-}
405+ handler e emap st =
406+ return
407+ $ Skip
408+ $ case Map.lookup e emap of
409+ Just i
410+ | i > 0 ->
411+ let emap1 = Map.insert e (i - 1) emap
412+ in RetryWithMap emap1 st
413+ | otherwise -> RetryDefault $ defaultHandler e
414+ Nothing -> RetryDefault $ defaultHandler e