module KnownRowSpec where

import Polysemy
import Polysemy.Error
import Polysemy.State
import Polysemy.Internal
import Polysemy.Internal.Union

import Test.Hspec

-- | A variant of 'runState' that uses 'stateToIO' if @r@ contains @Embed IO@.
-- (Can also be extended to check for @Final IO@)
runState' :: forall s r a. KnownRow r => s -> Sem (State s ': r) a -> Sem r (s, a)
runState' s sem = case tryMembership @(Embed IO) of
  Just proof -> subsumeUsing proof (stateToIO s (raiseUnder sem))
  _          -> runState s sem


test :: (Member (Error ()) r, KnownRow r)
     => Sem r String
test = fmap fst $ runState' "" $ do
  put "local state"
  _ <- (put "global state" >> throw ()) `catch` \() -> return ()
  return ()

spec :: Spec
spec = parallel $ describe "tryMembership" $ do
  it "should return a valid proof when the targeted \
     \ effect is part of the row" $ do
    res <- runM . runError @() $ test
    res `shouldBe` Right "global state"
  it "should not return a valid proof when the targeted \
     \ effect is not part of the row" $ do
    let res = run . runError @() $ test
    res `shouldBe` Right "local state"