{- |
Three-valued possible types for use with `aeson`.

Useful for use in PATCH endpoints: use in records which have 'ToJSON' and
'FromJSON' instances.

See the README for suggested usage.

The 'Alternative' instance can be used to update a record (with PATCH data) as
the LHS data is kept unless it is missing:

@
-- PATCH uses new data
'HaveData' new \<|\> old == 'HaveData' new

-- PATCH sets null
'HaveNull' \<|\> old == 'HaveNull'

-- PATCH did not change data
'Missing' \<|\> old == old
@
-}
module Data.Aeson.Possible (
    Possible (..),
    toMaybe,
    fromMaybeMaybe,
) where

import Control.Applicative
import Data.Aeson
import GHC.Generics (Generic)

data Possible a = Missing | HaveNull | HaveData a
    deriving stock (Int -> Possible a -> ShowS
[Possible a] -> ShowS
Possible a -> String
(Int -> Possible a -> ShowS)
-> (Possible a -> String)
-> ([Possible a] -> ShowS)
-> Show (Possible a)
forall a. Show a => Int -> Possible a -> ShowS
forall a. Show a => [Possible a] -> ShowS
forall a. Show a => Possible a -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: forall a. Show a => Int -> Possible a -> ShowS
showsPrec :: Int -> Possible a -> ShowS
$cshow :: forall a. Show a => Possible a -> String
show :: Possible a -> String
$cshowList :: forall a. Show a => [Possible a] -> ShowS
showList :: [Possible a] -> ShowS
Show, (forall x. Possible a -> Rep (Possible a) x)
-> (forall x. Rep (Possible a) x -> Possible a)
-> Generic (Possible a)
forall x. Rep (Possible a) x -> Possible a
forall x. Possible a -> Rep (Possible a) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall a x. Rep (Possible a) x -> Possible a
forall a x. Possible a -> Rep (Possible a) x
$cfrom :: forall a x. Possible a -> Rep (Possible a) x
from :: forall x. Possible a -> Rep (Possible a) x
$cto :: forall a x. Rep (Possible a) x -> Possible a
to :: forall x. Rep (Possible a) x -> Possible a
Generic, (forall a b. (a -> b) -> Possible a -> Possible b)
-> (forall a b. a -> Possible b -> Possible a) -> Functor Possible
forall a b. a -> Possible b -> Possible a
forall a b. (a -> b) -> Possible a -> Possible b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
$cfmap :: forall a b. (a -> b) -> Possible a -> Possible b
fmap :: forall a b. (a -> b) -> Possible a -> Possible b
$c<$ :: forall a b. a -> Possible b -> Possible a
<$ :: forall a b. a -> Possible b -> Possible a
Functor)

instance (Eq a) => Eq (Possible a) where
    Possible a
Missing == :: Possible a -> Possible a -> Bool
== Possible a
Missing = Bool
True
    Possible a
Missing == Possible a
_ = Bool
False
    Possible a
HaveNull == Possible a
HaveNull = Bool
True
    Possible a
HaveNull == Possible a
_ = Bool
False
    HaveData a
a == HaveData a
b = a
a a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== a
b
    HaveData a
_ == Possible a
_ = Bool
False

instance Applicative Possible where
    pure :: forall a. a -> Possible a
pure = a -> Possible a
forall a. a -> Possible a
HaveData
    (HaveData a -> b
f) <*> :: forall a b. Possible (a -> b) -> Possible a -> Possible b
<*> (HaveData a
x) = b -> Possible b
forall a. a -> Possible a
HaveData (a -> b
f a
x)
    Possible (a -> b)
HaveNull <*> Possible a
_ = Possible b
forall a. Possible a
HaveNull
    Possible (a -> b)
_ <*> Possible a
HaveNull = Possible b
forall a. Possible a
HaveNull
    Possible (a -> b)
Missing <*> Possible a
_ = Possible b
forall a. Possible a
Missing
    Possible (a -> b)
_ <*> Possible a
Missing = Possible b
forall a. Possible a
Missing

{- | Similar to the @Alternative Maybe@ instance, picks the leftmost 'HaveData'
value.
-}
instance Alternative Possible where
    empty :: forall a. Possible a
empty = Possible a
forall a. Possible a
Missing
    Possible a
HaveNull <|> :: forall a. Possible a -> Possible a -> Possible a
<|> Possible a
_ = Possible a
forall a. Possible a
HaveNull
    Possible a
Missing <|> Possible a
r = Possible a
r
    l :: Possible a
l@(HaveData a
_) <|> Possible a
_ = Possible a
l

{- | Uses 'toMaybe' to implement `toJSON` and `toEncoding`, and `aeson`'s
'omitField' to specify when the field should be left out.

/Note/ that unless the 'Possible' value is encoded as an object field it
will be `null` even when you have a 'Missing' value.
_e.g._ `[Missing, HaveNull, HaveData 42]` will be encoded as `[null,null,42]`
-}
instance (ToJSON a) => ToJSON (Possible a) where
    toJSON :: Possible a -> Value
toJSON = Maybe a -> Value
forall a. ToJSON a => a -> Value
toJSON (Maybe a -> Value)
-> (Possible a -> Maybe a) -> Possible a -> Value
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Possible a -> Maybe a
forall a. Possible a -> Maybe a
toMaybe
    toEncoding :: Possible a -> Encoding
toEncoding = Maybe a -> Encoding
forall a. ToJSON a => a -> Encoding
toEncoding (Maybe a -> Encoding)
-> (Possible a -> Maybe a) -> Possible a -> Encoding
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Possible a -> Maybe a
forall a. Possible a -> Maybe a
toMaybe
    omitField :: Possible a -> Bool
omitField Possible a
Missing = Bool
True
    omitField Possible a
HaveNull = Bool
False
    omitField (HaveData a
_) = Bool
False

-- | Uses `omittedField` to default to 'Missing'
instance (FromJSON a) => FromJSON (Possible a) where
    parseJSON :: Value -> Parser (Possible a)
parseJSON Value
Null = Possible a -> Parser (Possible a)
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Possible a
forall a. Possible a
HaveNull
    parseJSON Value
v = (a -> Possible a) -> Parser a -> Parser (Possible a)
forall a b. (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap a -> Possible a
forall a. a -> Possible a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Parser a -> Parser (Possible a))
-> (Value -> Parser a) -> Value -> Parser (Possible a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Value -> Parser a
forall a. FromJSON a => Value -> Parser a
parseJSON (Value -> Parser (Possible a)) -> Value -> Parser (Possible a)
forall a b. (a -> b) -> a -> b
$ Value
v
    omittedField :: Maybe (Possible a)
omittedField = Possible a -> Maybe (Possible a)
forall a. a -> Maybe a
Just Possible a
forall a. Possible a
Missing

toMaybe :: Possible a -> Maybe a
toMaybe :: forall a. Possible a -> Maybe a
toMaybe Possible a
Missing = Maybe a
forall a. Maybe a
Nothing
toMaybe Possible a
HaveNull = Maybe a
forall a. Maybe a
Nothing
toMaybe (HaveData a
a) = a -> Maybe a
forall a. a -> Maybe a
Just a
a

fromMaybeMaybe :: Maybe (Maybe a) -> Possible a
fromMaybeMaybe :: forall a. Maybe (Maybe a) -> Possible a
fromMaybeMaybe Maybe (Maybe a)
Nothing = Possible a
forall a. Possible a
Missing
fromMaybeMaybe (Just Maybe a
Nothing) = Possible a
forall a. Possible a
HaveNull
fromMaybeMaybe (Just (Just a
a)) = a -> Possible a
forall a. a -> Possible a
HaveData a
a