jordan-0.2.0.0: lib/Jordan/Generic/Options.hs
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Jordan.Generic.Options where
import Data.Coerce
import Data.Text (Text)
import qualified Data.Text as T
import Data.Type.Bool
import Data.Typeable (TypeRep, splitTyConApp, tyConModule, tyConName)
import GHC.Exts (Constraint)
import GHC.Generics
import GHC.TypeLits
type Representational (f :: * -> *) =
(forall a b. (Coercible a b) => Coercible (f a) (f b) :: Constraint)
data SumTypeEncoding
= TagVal
| TagInField
deriving (Show, Read, Eq, Ord, Bounded, Enum, Generic)
type family AllNullary cons where
AllNullary (C1 ('MetaCons _ _ 'False) (S1 ('MetaSel 'Nothing _ _ _) U1)) = True
AllNullary (a :+: b) = AllNullary a && AllNullary b
AllNullary _ = False
newtype PartOfSum f a = MkPartOfSum {getPartOfSum :: f a}
deriving (Show, Read, Eq, Ord, Generic)
-- | A newtype wrapper, designed to make it easier to derive ToJSON and FromJSON instances.
-- The API of abstract JSON serializing is awkward due to the somewhat bad ergonomics of the
-- 'Data.Functor.Contravariant.Divisible.Divisible' and (especially)
-- 'Data.Functor.Contravariant.Divisible.Decidable' typeclasses.
--
-- In general, using @ -XDerivingVia @, @ -XDeriveGeneric @, @ -XDataKinds @ and this wrapper will make your life much easier.
-- Unfortunately, due to a weird GHC quirk, you also need @ -XDerivingVia @.
--
-- That is, the following won't work, complaining about role errors:
--
-- @
-- data PersonFilter = PersonFilter { filterFirstName :: Maybe Text, filterLastName :: Maybe Text }
-- deriving (Show, Generic)
-- deriving (ToJSON, FromJSON) via (WithOptions '[KeepNothingFields] PersonFilter)
-- @
--
-- But this will:
--
-- @
-- data PersonFilter = PersonFilter { filterFirstName :: Maybe Text, filterLastName :: Maybe Text }
-- deriving (Show, Generic)
--
-- deriving via (WithOptions '[KeepNothingFields] PersonFilter) instance (ToJSON PersonFilter)
-- deriving via (WithOptions '[KeepNothingFields] PersonFilter) instance (FromJSON PersonFilter)
-- @
newtype WithOptions (options :: [*]) a = WithOptions {getWithOptions :: a}
deriving (Show, Eq, Ord)
-- | Newtype for use with GeneralizedNewtypeDeriving.
-- Will have us omit Nothing fields for parsing and serializing.
data OmitNothingFields = OmitNothingFields
-- | Keep nothing fields.
-- Will have us omit @ null @ when serializing Maybe types.
data KeepNothingFields = KeepNothingFields
fullyQualifyName ::
TypeRep ->
Text
fullyQualifyName tr =
case splitTyConApp tr of
(tc, []) -> baseName tc
(tc, args) -> baseName tc <> "(" <> T.intercalate "," (fullyQualifyName <$> args) <> ")"
where
baseName tc = T.pack (tyConModule tc <> "." <> tyConName tc)