module CfnFlip.Conduit
  ( takeBalancedC
  , module Conduit
  , module Data.Conduit.List
  ) where

import CfnFlip.Prelude

import Conduit
import Data.Conduit.List (sourceList)

-- | Take until an ending element, including any reopened-ended in between
--
-- >>> :{
-- runIdentity
--   $ runConduit
--   $ yieldMany "this is (a thing) here) and more"
--   .| takeBalancedC (== '(') (==')')
--   .| sinkList
-- :}
-- "this is (a thing) here)"
--
-- Note that imbalance will terminate early,
--
-- >>> :{
-- runIdentity
--   $ runConduit
--   $ yieldMany "this is, a) unexpected but b) the best we can do)"
--   .| takeBalancedC (== '(') (==')')
--   .| sinkList
-- :}
-- "this is, a)"
--
-- Or just run to the end
--
-- >>> :{
-- runIdentity
--   $ runConduit
--   $ yieldMany "this is (pretty unlikely"
--   .| takeBalancedC (== '(') (==')')
--   .| sinkList
-- :}
-- "this is (pretty unlikely"
--
takeBalancedC :: Monad m => (a -> Bool) -> (a -> Bool) -> ConduitT a a m ()
takeBalancedC :: forall (m :: * -> *) a.
Monad m =>
(a -> Bool) -> (a -> Bool) -> ConduitT a a m ()
takeBalancedC a -> Bool
reopens a -> Bool
closes = Int -> ConduitT a a m ()
go (Int
0 :: Int)
 where
  go :: Int -> ConduitT a a m ()
go Int
balance = do
    Maybe a
me <- forall (m :: * -> *) i o. Monad m => ConduitT i o m (Maybe i)
await

    forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
t a -> (a -> f b) -> f ()
for_ Maybe a
me forall a b. (a -> b) -> a -> b
$ \a
a -> do
      let
        loop :: ConduitT a a m ()
loop
          | a -> Bool
closes a
a = forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (Int
balance forall a. Ord a => a -> a -> Bool
<= Int
0) forall a b. (a -> b) -> a -> b
$ Int -> ConduitT a a m ()
go forall a b. (a -> b) -> a -> b
$ Int
balance forall a. Num a => a -> a -> a
- Int
1
          | a -> Bool
reopens a
a = Int -> ConduitT a a m ()
go forall a b. (a -> b) -> a -> b
$ Int
balance forall a. Num a => a -> a -> a
+ Int
1
          | Bool
otherwise = Int -> ConduitT a a m ()
go Int
balance

      forall (m :: * -> *) o i. Monad m => o -> ConduitT i o m ()
yield a
a forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> ConduitT a a m ()
loop