-- | Shared Aeson helpers for triple-state patch decoders. An omitted field leaves
-- the value unchanged. An explicit @null@ clears the override.
module Arbiter.Core.Json
  ( patchOptions
  , explicitOptionalField
  ) where

import Data.Aeson (FromJSON, Object, Options (..), defaultOptions, (.:))
import Data.Aeson.Key qualified as Key
import Data.Aeson.KeyMap qualified as KeyMap
import Data.Aeson.Types (Parser)
import Data.Text (Text)

-- | Generic options that omit @Nothing@ fields. A cleared override is absent
-- from the JSON.
patchOptions :: Options
patchOptions :: Options
patchOptions = Options
defaultOptions {omitNothingFields = True}

-- | Decode an override field with three states: absent (@Nothing@, leave unchanged),
-- present and @null@ (@Just Nothing@, clear), or present with a value (@Just (Just v)@).
explicitOptionalField :: (FromJSON a) => Object -> Text -> Parser (Maybe (Maybe a))
explicitOptionalField :: forall a. FromJSON a => Object -> Text -> Parser (Maybe (Maybe a))
explicitOptionalField Object
obj Text
name =
  let key :: Key
key = Text -> Key
Key.fromText Text
name
   in if Key -> Object -> Bool
forall a. Key -> KeyMap a -> Bool
KeyMap.member Key
key Object
obj
        then Maybe a -> Maybe (Maybe a)
forall a. a -> Maybe a
Just (Maybe a -> Maybe (Maybe a))
-> Parser (Maybe a) -> Parser (Maybe (Maybe a))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Object
obj Object -> Key -> Parser (Maybe a)
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
key
        else Maybe (Maybe a) -> Parser (Maybe (Maybe a))
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe (Maybe a)
forall a. Maybe a
Nothing