-- 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

{-# LANGUAGE RankNTypes #-}
-- | An assorted collection of functions useful when working with ProtoLens
-- protocol buffers.  These functions are inspired by functionality found in
-- the protobuf implementation in other languages.
module Data.ProtoLens.Combinators
    ( has
    , clear
    , make
    , modifyInState
    ) where

import Control.Monad.Trans.State (State, execState)
import Data.ProtoLens (Message(..))
import Data.Maybe (isJust)
import Lens.Family2 (LensLike, Phantom, Setter, to, (.~))

-- | Turns a 'LensLike' getting a 'Maybe' into a 'Getter' of a 'Bool'
-- that returns @True@ when the 'Maybe' is @Just@ something.
--
-- I.e., makes a getter for a 'Maybe' field that returns whether it's set.
has :: Phantom f => LensLike f a a' (Maybe b) b' -> LensLike f a a' Bool b''
has :: forall (f :: * -> *) a a' b b' b''.
Phantom f =>
LensLike f a a' (Maybe b) b' -> LensLike f a a' Bool b''
has = (((Maybe b -> f b') -> a -> f a')
-> ((Bool -> f b'') -> Maybe b -> f b')
-> (Bool -> f b'')
-> a
-> f a'
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Maybe b -> Bool) -> Getter (Maybe b) b' Bool b''
forall s a t b. (s -> a) -> Getter s t a b
to Maybe b -> Bool
forall a. Maybe a -> Bool
isJust)

-- | Sets the provided lens in @a@ to @Nothing@.
clear :: Setter a a' b (Maybe b') -> a -> a'
clear :: forall a a' b b'. Setter a a' b (Maybe b') -> a -> a'
clear Setter a a' b (Maybe b')
setter = LensLike f a a' b (Maybe b')
Setter a a' b (Maybe b')
setter Setter a a' b (Maybe b') -> Maybe b' -> a -> a'
forall s t a b. Setter s t a b -> b -> s -> t
.~ Maybe b'
forall a. Maybe a
Nothing

-- | Creates a 'Default' and then applies the provided `State` to it.  This is
-- convenient for creating complicated structures.
make :: Message msg => State msg a -> msg
make :: forall msg a. Message msg => State msg a -> msg
make = msg -> State msg a -> msg
forall s a. s -> State s a -> s
modifyInState msg
forall msg. Message msg => msg
defMessage

-- | Allows one to modify a value in the 'State' monad.  Note that this is
-- just for syntactic convenience with @do@ blocks, e.g.
--
-- @
--    newThing = modifyInState thing $ do
--        ...
-- @
modifyInState :: s -> State s a -> s
modifyInState :: forall s a. s -> State s a -> s
modifyInState = (State s a -> s -> s) -> s -> State s a -> s
forall a b c. (a -> b -> c) -> b -> a -> c
flip State s a -> s -> s
forall s a. State s a -> s -> s
execState