content: {-# language FlexibleContexts #-}
  
  module Rel8.Query.Either
    ( keepLeftTable
    , keepRightTable
    , bitraverseEitherTable
    )
  where
  
  -- base
  import Prelude
  
  -- comonad
  import Control.Comonad ( extract )
  
  -- rel8
  import Rel8.Expr ( Expr )
  import Rel8.Expr.Eq ( (==.) )
  import Rel8.Query ( Query )
  import Rel8.Query.Filter ( where_ )
  import Rel8.Query.Maybe ( optional )
  import Rel8.Table.Either
    ( EitherTable( EitherTable )
    , isLeftTable, isRightTable
    )
  import Rel8.Table.Maybe ( MaybeTable( MaybeTable ), isJustTable )
  
  
  -- | Filter 'EitherTable's, keeping only 'leftTable's.
  keepLeftTable :: EitherTable Expr a b -> Query a
  keepLeftTable e@(EitherTable _ a _) = do
    where_ $ isLeftTable e
    pure (extract a)
  
  
  -- | Filter 'EitherTable's, keeping only 'rightTable's.
  keepRightTable :: EitherTable Expr a b -> Query b
  keepRightTable e@(EitherTable _ _ b) = do
    where_ $ isRightTable e
    pure (extract b)
  
  
  -- | @bitraverseEitherTable f g x@ will pass all @leftTable@s through @f@ and
  -- all @rightTable@s through @g@. The results are then lifted back into
  -- @leftTable@ and @rightTable@, respectively. This is similar to 'bitraverse'
  -- for 'Either'.
  --
  -- For example,
  --
  -- >>> :{
  -- select do
  --   x <- values (map lit [ Left True, Right (42 :: Int32) ])
  --   bitraverseEitherTable (\y -> values [y, not_ y]) (\y -> pure (y * 100)) x
  -- :}
  -- [ Left True
  -- , Left False
  -- , Right 4200
  -- ]
  bitraverseEitherTable :: ()
    => (a -> Query c)
    -> (b -> Query d)
    -> EitherTable Expr a b
    -> Query (EitherTable Expr c d)
  bitraverseEitherTable f g e@(EitherTable tag _ _) = do
    mc@(MaybeTable _ c) <- optional (f =<< keepLeftTable e)
    md@(MaybeTable _ d) <- optional (g =<< keepRightTable e)
    where_ $ isJustTable mc ==. isLeftTable e
    where_ $ isJustTable md ==. isRightTable e
    pure $ EitherTable tag c d
  