graphql-engine/server/src-lib/Hasura/RQL/DDL/Webhook/Transform/WithOptional.hs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

73 lines
2.3 KiB
Haskell
Raw Normal View History

-- | The 'WithOptional' Functor and associated operations.
module Hasura.RQL.DDL.Webhook.Transform.WithOptional
( WithOptional (..),
withOptional,
withOptionalField',
withOptionalFieldWith',
)
where
-------------------------------------------------------------------------------
import Autodocodec (HasCodec (codec), ObjectCodec, ValueCodec, dimapCodec, optionalFieldWith')
import Data.Aeson (FromJSON, ToJSON)
import Data.Coerce (Coercible)
import Hasura.Prelude
-------------------------------------------------------------------------------
-- | Enrich a 'Functor' @f@ with optionality; this is primarily useful when
-- one wants to annotate fields as optional when using the Higher-Kinded Data
-- pattern.
--
-- 'WithOptional'@ f@ is equivalent to @Compose Maybe f@.
newtype WithOptional f result = WithOptional
{ getOptional :: Maybe (f result)
}
deriving stock (Eq, Functor, Foldable, Generic, Show)
deriving newtype (FromJSON, ToJSON)
deriving newtype instance
(NFData (f result)) =>
NFData (WithOptional f result)
-- | 'WithOptional' smart constructor for the special case of optional values
-- that are representationally equivalent to some "wrapper" type.
--
-- For example:
-- @
-- withOptional \@HeaderTransformsAction headers == WithOptional $ fmap HeadersTransform headers
-- @
--
-- In other words: this function observes the isomorphism between @'Maybe' a@
-- and @'WithOptional' f b@ if an isomorphism exists between @a@ and @f b@.
withOptional ::
forall a b f.
Coercible a (f b) =>
Maybe a ->
WithOptional f b
withOptional = coerce
-- | Define a field in an object codec that applies 'withOptional' when
-- decoding, and applies 'getOptional' when encoding.
withOptionalField' ::
forall a b f.
(Coercible a (f b), HasCodec a) =>
Text ->
ObjectCodec (WithOptional f b) (WithOptional f b)
withOptionalField' name = withOptionalFieldWith' name (codec @a)
-- | Define a field in an object codec that applies 'withOptional' when
-- decoding, and applies 'getOptional' when encoding.
--
-- This version takes a codec for the underlying value type as an argument.
withOptionalFieldWith' ::
forall a b f.
Coercible a (f b) =>
Text ->
ValueCodec a a ->
ObjectCodec (WithOptional f b) (WithOptional f b)
withOptionalFieldWith' name aCodec =
dimapCodec withOptional (fmap coerce . getOptional) $
optionalFieldWith' name aCodec