-- | Options that influence TH code generation
module Data.Record.TH.Config.Options (
    Options(..)
  , defaultStrictOptions
  , defaultLazyOptions
  , defaultPureScript
  ) where

{-------------------------------------------------------------------------------
  Options
-------------------------------------------------------------------------------}

-- | Tweak the output of the generator
--
-- In the explanations of the various options below, we will use the following
-- record as our running example:
--
-- > data T a b = MkT {
-- >       tWord  :: Word
-- >     , tBool  :: Bool
-- >     , tChar  :: Char
-- >     , tA     :: a
-- >     , tListB :: [b]
-- >     }
-- >   deriving (Eq, Show)
data Options = Options {
      -- | Generate a pattern synonym for the record
      --
      -- > pattern MkT :: Word -> Bool -> Char -> a -> [b] -> T a b
      -- > pattern MkT{tInt, tBool, tChar, tA, tListB} <- ..
      -- >   where
      -- >     MkT tInt' tBool' tChar' tA' tListB' = ..
      --
      -- The pattern synonym makes it possible to construct or pattern match on
      -- @T@ values as if it had been defined like a normal record.
      --
      -- We do /not/ do this by default, however, because unfortunately when
      -- we define a record pattern synonym in @ghc@, @ghc@ also (unnecessarily
      -- but currently unavoidably) introduces field accessors for all fields
      -- in the record, and we're back to code that is quadratic in size.
      --
      -- Avoid if possible.
      Options -> Bool
generatePatternSynonym :: Bool

      -- | Generate a "constructor function" for the record
      --
      -- > _construct_MkT :: Word -> Bool -> Char -> a -> [b] -> T a b
      -- > _construct_MkT = ..
      --
      -- This function can be used directly, but it is also used by the 'lr'
      -- quasi-quoter, so if this function is not generated, 'lr' will not work.
    , Options -> Bool
generateConstructorFn :: Bool

      -- | Generate 'HasField' instances
      --
      -- > instance HasField "tInt" (T a b) Word where
      -- >   hasField = ..
      --
      -- These are required by the @record-dot-preprocessor@.
    , Options -> Bool
generateHasFieldInstances :: Bool

      -- | Generate field accessors
      --
      -- > tInt :: T a b -> Word
      -- > tInt = ..
      --
      -- If field accessors are not generated, the only way to access fields
      -- is through the 'HasField' instances.
      --
      -- Disabling this option is primarily useful if you need overloading:
      -- if you have multiple records with a field of the same name, then
      -- generating field accessors would result in name clashes. Without the
      -- accessors, overloading is resolved through 'HasField'.
    , Options -> Bool
generateFieldAccessors :: Bool

      -- | Make all fields strict
      --
      -- This should be used when using the @StrictData@ or @Strict@ language
      -- extension.
    , Options -> Bool
allFieldsStrict :: Bool
    }

{-------------------------------------------------------------------------------
  Defaults
-------------------------------------------------------------------------------}

defaultLazyOptions :: Options
defaultLazyOptions :: Options
defaultLazyOptions = Options :: Bool -> Bool -> Bool -> Bool -> Bool -> Options
Options {
      generatePatternSynonym :: Bool
generatePatternSynonym    = Bool
False
    , generateConstructorFn :: Bool
generateConstructorFn     = Bool
True
    , generateHasFieldInstances :: Bool
generateHasFieldInstances = Bool
True
    , generateFieldAccessors :: Bool
generateFieldAccessors    = Bool
True
    , allFieldsStrict :: Bool
allFieldsStrict           = Bool
False
    }

defaultStrictOptions :: Options
defaultStrictOptions :: Options
defaultStrictOptions = Options :: Bool -> Bool -> Bool -> Bool -> Bool -> Options
Options {
      generatePatternSynonym :: Bool
generatePatternSynonym    = Bool
False
    , generateConstructorFn :: Bool
generateConstructorFn     = Bool
True
    , generateHasFieldInstances :: Bool
generateHasFieldInstances = Bool
True
    , generateFieldAccessors :: Bool
generateFieldAccessors    = Bool
True
    , allFieldsStrict :: Bool
allFieldsStrict           = Bool
True
    }

-- | Default options for "Purescript style" records
--
-- That is:
--
-- * All fields are strict
-- * We do /not/ generate field accessors: fields must be accessed and updated
--   through the 'HasField' instances (e.g., @record-dot-preprocessor@ syntax).
--
-- We do not introduce a pattern synonym by default:
--
-- * Introducing a pattern synonym reintroduces code that is quadratic in size.
-- * Perhaps more importantly, it would make it impossible to define two records
--   with the same field names in a single module, as the field accessors
--   (unnecessarily but currently unavoidably) introduced by the pattern synonym
--   would clash.
--
-- NOTE: The @record-dot-preprocessor@ enables @DuplicateRecordFields@ by
-- default. Since the records that we produce are not visible to @ghc@,
-- @large-records@ is not compatible with DRF-style overloading. However, as
-- long as all overloading is resolved through @HasField@ instead (which is
-- what @record-dot-preprocessor@ encourages anyway), all is fine.
defaultPureScript :: Options
defaultPureScript :: Options
defaultPureScript = Options :: Bool -> Bool -> Bool -> Bool -> Bool -> Options
Options {
      generatePatternSynonym :: Bool
generatePatternSynonym    = Bool
False
    , generateConstructorFn :: Bool
generateConstructorFn     = Bool
True
    , generateHasFieldInstances :: Bool
generateHasFieldInstances = Bool
True
    , generateFieldAccessors :: Bool
generateFieldAccessors    = Bool
False
    , allFieldsStrict :: Bool
allFieldsStrict           = Bool
True
    }