content: -- Copyright 2016 Google Inc. All Rights Reserved.
  --
  -- Use of this source code is governed by a BSD-style
  -- license that can be found in the LICENSE file or at
  -- https://developers.google.com/open-source/licenses/bsd
  
  -- | This module defines the 'Prism' type and combinators. Used for building
  --   'Prism's for oneof fields.
  {-# LANGUAGE RankNTypes #-}
  
  module Data.ProtoLens.Prism
      ( Prism
      , Prism'
      , AReview
      , (#)
      , prism
      , prism'
      , _Left
      , _Right
      , _Just
      , _Nothing
      ) where
  
  import  Data.Tagged (Tagged (..))
  import  Data.Functor.Identity (Identity (..))
  import  Data.Profunctor (dimap)
  import  Data.Profunctor.Choice
  import  Data.Profunctor.Unsafe ((#.), (.#))
  
  
  ------------------------------------------------------------------------------
  -- Prism Internals
  ------------------------------------------------------------------------------
  type Prism s t a b = forall p f. (Choice p, Applicative f) => p a (f b) -> p s (f t)
  
  type Prism' s a = Prism s s a a
  
  type Optic p f s t a b = p a (f b) -> p s (f t)
  
  type Optic' p f s a = Optic p f s s a a
  
  type AReview t b = Optic' Tagged Identity t b
  
  -- | Used for constructing 'Prism' values.
  --
  -- @'_Just' '#' 5 == 'Just' 5@
  ( # ) :: AReview t b -> b -> t
  ( # ) p = runIdentity #. unTagged #. p .# Tagged .# Identity
  infixr 8 #
  
  
  ------------------------------------------------------------------------------
  -- Prism Combinators
  ------------------------------------------------------------------------------
  
  -- | Build a 'Control.Lens.Prism.Prism'.
  --
  -- @'Either' t a@ is used instead of @'Maybe' a@ to permit the types of @s@ and @t@ to differ.
  --
  prism :: (b -> t) -> (s -> Either t a) -> Prism s t a b
  prism bt seta = dimap seta (either pure (fmap bt)) . right'
  {-# INLINE prism #-}
  
  -- | This is usually used to build a 'Prism'', when you have to use an operation like
  -- 'Data.Typeable.cast' which already returns a 'Maybe'.
  prism' :: (b -> s) -> (s -> Maybe a) -> Prism s s a b
  prism' bs sma = prism bs (\s -> maybe (Left s) Right (sma s))
  {-# INLINE prism' #-}
  
  _Left :: Prism (Either a c) (Either b c) a b
  _Left = prism Left $ either Right (Left . Right)
  
  _Right :: Prism (Either c a) (Either c b) a b
  _Right = prism Right $ either (Left . Left) Right
  
  _Just :: Prism (Maybe a) (Maybe b) a b
  _Just = prism Just $ maybe (Left Nothing) Right
  
  _Nothing :: Prism' (Maybe a) ()
  _Nothing = prism' (const Nothing) $ maybe (Just ()) (const Nothing)
  