module UnliftIO.MessageBox.Util.Future
  ( Future (Future),
    tryNow,
    awaitFuture,
  )
where

import UnliftIO (MonadIO (liftIO), MonadUnliftIO)
import UnliftIO.Concurrent (threadDelay)

-- | A wrapper around an IO action that returns value
-- in the future.
newtype Future a = Future
  { -- | Return 'Just' the value or 'Nothing',
    --   when the value is not available yet.
    Future a -> IO (Maybe a)
fromFuture :: IO (Maybe a)
  }

-- | Return 'Just' the value or 'Nothing',
--   when the value is not available yet.
--
--   Once the value is available, that value
--   will be returned everytime this function is
--   invoked.
{-# INLINE tryNow #-}
tryNow :: MonadUnliftIO m => Future a -> m (Maybe a)
tryNow :: Future a -> m (Maybe a)
tryNow = IO (Maybe a) -> m (Maybe a)
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (Maybe a) -> m (Maybe a))
-> (Future a -> IO (Maybe a)) -> Future a -> m (Maybe a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Future a -> IO (Maybe a)
forall a. Future a -> IO (Maybe a)
fromFuture

-- | Poll a Future until the value is present.
awaitFuture :: MonadUnliftIO m => Future b -> m b
awaitFuture :: Future b -> m b
awaitFuture !Future b
f =
  Future b -> m (Maybe b)
forall (m :: * -> *) a. MonadUnliftIO m => Future a -> m (Maybe a)
tryNow Future b
f m (Maybe b) -> (Maybe b -> m b) -> m b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= m b -> (b -> m b) -> Maybe b -> m b
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (Int -> m ()
forall (m :: * -> *). MonadIO m => Int -> m ()
threadDelay Int
10 m () -> m b -> m b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Future b -> m b
forall (m :: * -> *) b. MonadUnliftIO m => Future b -> m b
awaitFuture Future b
f) b -> m b
forall (m :: * -> *) a. Monad m => a -> m a
return