content: {-# LANGUAGE AllowAmbiguousTypes  #-}
  {-# LANGUAGE CPP                  #-}
  {-# LANGUAGE DataKinds            #-}
  {-# LANGUAGE FlexibleContexts     #-}
  {-# LANGUAGE FlexibleInstances    #-}
  {-# LANGUAGE GADTs                #-}
  {-# LANGUAGE PolyKinds            #-}
  {-# LANGUAGE Rank2Types           #-}
  {-# LANGUAGE TypeOperators        #-}
  {-# LANGUAGE TypeSynonymInstances #-}
  {-# LANGUAGE UndecidableInstances #-}
  
  {- |
  Module                  : Toml.Codec.Generic
  Copyright               : (c) 2018-2022 Kowainik
  SPDX-License-Identifier : MPL-2.0
  Maintainer              : Kowainik <xrom.xkov@gmail.com>
  Stability               : Stable
  Portability             : Portable
  
  This module contains implementation of the 'Generic' TOML codec. If your
  data types are big and nested, and you want to have codecs for them without writing a lot of
  boilerplate code, you can find this module helpful. Below you can find the detailed
  explanation on how the 'Generic' codecs work.
  
  Consider the following Haskell data types:
  
  @
  __data__ User = User
      { age     :: Int
      , address :: Address
      , socials :: [Social]
      } __deriving__ ('Generic')
  
  __data__ Address = Address
      { street :: Text
      , house  :: Int
      } __deriving__ ('Generic')
  
  __data__ Social = Social
      { name :: Text
      , link :: Text
      } __deriving__ ('Generic')
  @
  
  Value of the @User@ type represents the following TOML:
  
  @
  age = 27
  
  [address]
    street = "Miami Beach"
    house  = 42
  
  [[socials]]
    name = \"Twitter\"
    link = "https://twitter.com/foo"
  
  [[socials]]
    name = \"GitHub\"
    link = "https://github.com/bar"
  @
  
  Normally you would write 'TomlCodec' for this data type like this:
  
  @
  userCodec :: 'TomlCodec' User
  userCodec = User
      \<$\> Toml.int "age" .= age
      \<*\> Toml.table addressCodec "address" .= address
      \<*\> Toml.list  socialCodec  "socials" .= socials
  
  addressCodec :: 'TomlCodec' Address
  addressCodec = Address
      \<$\> Toml.text "street" .= street
      \<*\> Toml.int  "house"  .= house
  
  socialCodec :: 'TomlCodec' Social
  socialCodec = Social
      \<$\> Toml.text "name" .= name
      \<*\> Toml.text "link" .= link
  @
  
  However, if you derive 'Generic' instance for your data types (as we do in the
  example), you can write your codecs in a simpler way.
  
  @
  userCodec :: 'TomlCodec' User
  userCodec = 'genericCodec'
  
  __instance__ 'HasCodec' Address __where__
      hasCodec = Toml.table 'genericCodec'
  
  __instance__ 'HasItemCodec' Social __where__
      hasItemCodec = Right 'genericCodec'
  @
  
  Several notes about the interface:
  
  1. Your top-level data types are always implemented as 'genericCodec' (or other
  generic codecs).
  2. If you have a custom data type as a field of another type, you need to implement
  the instance of the 'HasCodec' typeclass.
  3. If the data type appears as an element of a list, you need to implement the instance
  of the 'HasItemCodec' typeclass.
  
  @since 1.1.0.0
  -}
  
  module Toml.Codec.Generic
         ( genericCodec
         , genericCodecWithOptions
         , stripTypeNameCodec
  
           -- * Options
         , TomlOptions (..)
         , GenericOptions (..)
         , stripTypeNameOptions
         , stripTypeNamePrefix
  
           -- * Core generic typeclass
         , HasCodec (..)
         , HasItemCodec (..)
         , GenericCodec (..)
  
           -- * 'ByteString' newtypes
           -- $bytestring
         , ByteStringAsText (..)
         , ByteStringAsBytes (..)
         , LByteStringAsText (..)
         , LByteStringAsBytes (..)
  
           -- * Deriving Via
         , TomlTable (..)
         , TomlTableStrip (..)
         ) where
  
  import Data.ByteString (ByteString)
  import Data.Char (isLower, toLower)
  import Data.Coerce (coerce)
  import Data.HashMap.Strict (HashMap)
  import Data.HashSet (HashSet)
  import Data.Hashable (Hashable)
  import Data.IntMap.Strict (IntMap)
  import Data.IntSet (IntSet)
  import Data.Kind (Type)
  import Data.List (stripPrefix)
  import Data.List.NonEmpty (NonEmpty)
  import Data.Map.Strict (Map)
  import Data.Monoid (All (..), Any (..), First (..), Last (..), Product (..), Sum (..))
  import Data.Proxy (Proxy (..))
  import Data.Set (Set)
  import Data.String (IsString (..))
  import Data.Text (Text)
  import Data.Time (Day, LocalTime, TimeOfDay, ZonedTime)
  import Data.Typeable (Typeable, typeRep)
  import Data.Word (Word8)
  import GHC.Generics (C1, D1, Generic (..), K1 (..), M1 (..), Rec0, S1, Selector (..), (:*:) (..),
                       (:+:))
  import GHC.TypeLits (ErrorMessage (..), TypeError)
  import Numeric.Natural (Natural)
  
  import Toml.Codec.BiMap (TomlBiMap)
  import Toml.Codec.Di (diwrap, (.=))
  import Toml.Codec.Types (TomlCodec)
  import Toml.Type.AnyValue (AnyValue)
  import Toml.Type.Key (Key)
  
  import qualified Data.ByteString.Lazy as LBS
  import qualified Data.Text.Lazy as L
  
  import qualified Toml.Codec.BiMap.Conversion as Toml
  import qualified Toml.Codec.Combinator as Toml
  import qualified Toml.Codec.Di as Toml
  
  
  {- | Generic codec for arbitrary data types. Uses field names as keys.
  
  @since 1.1.0.0
  -}
  genericCodec :: (Generic a, GenericCodec (Rep a)) => TomlCodec a
  genericCodec = Toml.dimap from to $ genericTomlCodec (GenericOptions id)
  {-# INLINE genericCodec #-}
  
  {- | Generic codec with options for arbitrary data types.
  
  @since 1.1.0.0
  -}
  genericCodecWithOptions
      :: forall a
       . (Generic a, GenericCodec (Rep a), Typeable a)
      => TomlOptions a
      -> TomlCodec a
  genericCodecWithOptions = Toml.dimap from to . genericTomlCodec . toGenericOptions @a
  {-# INLINE genericCodecWithOptions #-}
  
  {- | Generic codec that uses 'stripTypeNameOptions'.
  
  @since 1.1.0.0
  -}
  stripTypeNameCodec
      :: forall a
       . (Generic a, GenericCodec (Rep a), Typeable a)
      => TomlCodec a
  stripTypeNameCodec = genericCodecWithOptions $ stripTypeNameOptions @a
  {-# INLINE stripTypeNameCodec #-}
  
  ----------------------------------------------------------------------------
  -- Generic typeclasses
  ----------------------------------------------------------------------------
  
  {- | Options to configure various parameters of generic encoding. Specifically:
  
  *  __'tomlOptionsFieldModifier'__: how to translate field names to TOML keys?
  
  @since 1.1.0.0
  -}
  data TomlOptions a = TomlOptions
      { tomlOptionsFieldModifier :: Typeable a => Proxy a -> String -> String
      }
  
  {- | Same as 'TomlOptions' but with all data type information erased. This data
  type is used internally. Define your options using 'TomlOptions' data type.
  
  @since 1.1.0.0
  -}
  newtype GenericOptions = GenericOptions
      { genericOptionsFieldModifier :: String -> String
      }
  
  toGenericOptions :: forall a . Typeable a => TomlOptions a -> GenericOptions
  toGenericOptions TomlOptions{..} = GenericOptions
      { genericOptionsFieldModifier = tomlOptionsFieldModifier (Proxy @a)
      }
  
  {- | Options that use 'stripTypeNamePrefix' as 'tomlOptionsFieldModifier'.
  
  @since 1.1.0.0
  -}
  stripTypeNameOptions :: Typeable a => TomlOptions a
  stripTypeNameOptions = TomlOptions
      { tomlOptionsFieldModifier = stripTypeNamePrefix
      }
  
  {- | Strips name of the type name from field name prefix.
  
  >>> data UserData = UserData { userDataId :: Int, userDataShortInfo :: Text }
  >>> stripTypeNamePrefix (Proxy @UserData) "userDataId"
  "id"
  >>> stripTypeNamePrefix (Proxy @UserData) "userDataShortInfo"
  "shortInfo"
  >>> stripTypeNamePrefix (Proxy @UserData) "udStats"
  "stats"
  >>> stripTypeNamePrefix (Proxy @UserData) "fooBar"
  "bar"
  >>> stripTypeNamePrefix (Proxy @UserData) "name"
  "name"
  
  @since 1.1.0.0
  -}
  stripTypeNamePrefix :: forall a . Typeable a => Proxy a -> String -> String
  stripTypeNamePrefix _ fieldName =
      case stripPrefix (headToLower $ typeName @a) fieldName of
          Just rest -> leaveIfEmpty rest
          Nothing   -> leaveIfEmpty (dropWhile isLower fieldName)
    where
      headToLower :: String -> String
      headToLower = \case
          []   -> error "Cannot use 'headToLower' on empty Text"
          x:xs -> toLower x : xs
  
      -- if all lower case then leave field as it is
      leaveIfEmpty :: String -> String
      leaveIfEmpty rest = if null rest then fieldName else headToLower rest
  
  typeName :: forall a . Typeable a => String
  typeName = show $ typeRep (Proxy @a)
  
  ----------------------------------------------------------------------------
  -- Generic typeclasses
  ----------------------------------------------------------------------------
  
  {- | Helper class to derive TOML codecs generically.
  
  @since 1.1.0.0
  -}
  class GenericCodec (f :: k -> Type) where
      genericTomlCodec :: GenericOptions -> TomlCodec (f p)
  
  -- | @since 1.1.0.0
  instance GenericCodec f => GenericCodec (D1 d f) where
      genericTomlCodec = Toml.dimap unM1 M1 . genericTomlCodec
      {-# INLINE genericTomlCodec #-}
  
  type GenericSumTomlNotSupported =
      'Text "Generic TOML deriving for arbitrary sum types is not supported currently."
  
  -- | @since 1.1.0.0
  instance (TypeError GenericSumTomlNotSupported) => GenericCodec (f :+: g) where
      genericTomlCodec = error "Not supported"
  
  -- | @since 1.1.0.0
  instance GenericCodec f => GenericCodec (C1 c f) where
      genericTomlCodec = Toml.dimap unM1 M1 . genericTomlCodec
      {-# INLINE genericTomlCodec #-}
  
  -- | @since 1.1.0.0
  instance (GenericCodec f, GenericCodec g) => GenericCodec (f :*: g) where
      genericTomlCodec options = (:*:)
          <$> genericTomlCodec options .= fstG
          <*> genericTomlCodec options .= sndG
        where
          fstG :: (f :*: g) p -> f p
          fstG (f :*: _) = f
  
          sndG :: (f :*: g) p -> g p
          sndG (_ :*: g) = g
      {-# INLINE genericTomlCodec #-}
  
  -- | @since 1.1.0.0
  instance (Selector s, HasCodec a) => GenericCodec (S1 s (Rec0 a)) where
      genericTomlCodec GenericOptions{..} = genericWrap $ hasCodec @a fieldName
        where
          genericWrap :: TomlCodec a -> TomlCodec (S1 s (Rec0 a) p)
          genericWrap = Toml.dimap (unK1 . unM1) (M1 . K1)
  
          fieldName :: Key
          fieldName =
              fromString
              $ genericOptionsFieldModifier
              $ selName (error "S1" :: S1 s Proxy ())
      {-# INLINE genericTomlCodec #-}
  
  ----------------------------------------------------------------------------
  -- Helper typeclasses
  ----------------------------------------------------------------------------
  
  {- | This typeclass tells how the data type should be coded as an item of a
  list. Lists in TOML can have two types: __primitive__ and __table of arrays__.
  
  * If 'hasItemCodec' returns 'Left': __primitive__ arrays codec is used.
  * If 'hasItemCodec' returns 'Right:' __table of arrays__ codec is used.
  
  @since 1.1.0.0
  -}
  class HasItemCodec a where
      hasItemCodec :: Either (TomlBiMap a AnyValue) (TomlCodec a)
  
  -- | @since 1.1.0.0
  instance HasItemCodec Bool where
      hasItemCodec = Left Toml._Bool
      {-# INLINE hasItemCodec #-}
  
  -- | @since 1.1.0.0
  instance HasItemCodec Int where
      hasItemCodec = Left Toml._Int
      {-# INLINE hasItemCodec #-}
  
  -- | @since 1.1.0.0
  instance HasItemCodec Word where
      hasItemCodec = Left Toml._Word
      {-# INLINE hasItemCodec #-}
  
  -- | @since 1.2.0.0
  instance HasItemCodec Word8 where
      hasItemCodec = Left Toml._Word8
      {-# INLINE hasItemCodec #-}
  
  -- | @since 1.1.0.0
  instance HasItemCodec Integer where
      hasItemCodec = Left Toml._Integer
      {-# INLINE hasItemCodec #-}
  
  -- | @since 1.1.0.0
  instance HasItemCodec Natural where
      hasItemCodec = Left Toml._Natural
      {-# INLINE hasItemCodec #-}
  
  -- | @since 1.1.0.0
  instance HasItemCodec Double where
      hasItemCodec = Left Toml._Double
      {-# INLINE hasItemCodec #-}
  
  -- | @since 1.1.0.0
  instance HasItemCodec Float where
      hasItemCodec = Left Toml._Float
      {-# INLINE hasItemCodec #-}
  
  -- | @since 1.1.0.0
  instance HasItemCodec Text where
      hasItemCodec = Left Toml._Text
      {-# INLINE hasItemCodec #-}
  
  -- | @since 1.1.0.0
  instance HasItemCodec L.Text where
      hasItemCodec = Left Toml._LText
      {-# INLINE hasItemCodec #-}
  
  -- | @since 1.3.0.0
  instance HasItemCodec ByteStringAsText where
      hasItemCodec = Left $ coerce Toml._ByteString
      {-# INLINE hasItemCodec #-}
  
  -- | @since 1.3.0.0
  instance HasItemCodec ByteStringAsBytes where
      hasItemCodec = Left $ coerce Toml._ByteStringArray
      {-# INLINE hasItemCodec #-}
  
  -- | @since 1.3.0.0
  instance HasItemCodec LByteStringAsText where
      hasItemCodec = Left $ coerce Toml._LByteString
      {-# INLINE hasItemCodec #-}
  
  -- | @since 1.3.0.0
  instance HasItemCodec LByteStringAsBytes where
      hasItemCodec = Left $ coerce Toml._LByteStringArray
      {-# INLINE hasItemCodec #-}
  
  -- | @since 1.1.0.0
  instance HasItemCodec ZonedTime where
      hasItemCodec = Left Toml._ZonedTime
      {-# INLINE hasItemCodec #-}
  
  -- | @since 1.1.0.0
  instance HasItemCodec LocalTime where
      hasItemCodec = Left Toml._LocalTime
      {-# INLINE hasItemCodec #-}
  
  -- | @since 1.1.0.0
  instance HasItemCodec Day where
      hasItemCodec = Left Toml._Day
      {-# INLINE hasItemCodec #-}
  
  -- | @since 1.1.0.0
  instance HasItemCodec TimeOfDay where
      hasItemCodec = Left Toml._TimeOfDay
      {-# INLINE hasItemCodec #-}
  
  -- | @since 1.1.0.0
  instance HasItemCodec IntSet where
      hasItemCodec = Left Toml._IntSet
      {-# INLINE hasItemCodec #-}
  
  {- | If data type @a@ is not primitive then this instance returns codec for list
  under key equal to @a@ type name.
  
  @since 1.1.0.0
  -}
  instance (HasItemCodec a, Typeable a) => HasItemCodec [a] where
      hasItemCodec = case hasItemCodec @a of
          Left prim   -> Left $ Toml._Array prim
          Right codec -> Right $ Toml.list codec (fromString $ typeName @a)
      {-# INLINE hasItemCodec #-}
  
  {- | Helper typeclass for generic deriving. This instance tells how the data
  type should be coded if it's a field of another data type.
  
  __NOTE:__ If you implement TOML codecs for your data types manually, prefer more
  explicit @Toml.int@ or @Toml.text@ instead of implicit @Toml.hasCodec@.
  Implement instances of this typeclass only when using 'genericCodec' and when
  your custom data types are not covered here.
  
  @since 1.1.0.0
  -}
  class HasCodec a where
      hasCodec :: Key -> TomlCodec a
  
  -- | @since 1.1.0.0
  instance HasCodec Bool where
      hasCodec = Toml.bool
      {-# INLINE hasCodec #-}
  
  -- | @since 1.1.0.0
  instance HasCodec Int where
      hasCodec = Toml.int
      {-# INLINE hasCodec #-}
  
  -- | @since 1.1.0.0
  instance HasCodec Word where
      hasCodec = Toml.word
      {-# INLINE hasCodec #-}
  
  -- | @since 1.2.0.0
  instance HasCodec Word8 where
      hasCodec = Toml.word8
      {-# INLINE hasCodec #-}
  
  -- | @since 1.1.0.0
  instance HasCodec Integer where
      hasCodec = Toml.integer
      {-# INLINE hasCodec #-}
  
  -- | @since 1.1.0.0
  instance HasCodec Natural where
      hasCodec = Toml.natural
      {-# INLINE hasCodec #-}
  
  -- | @since 1.1.0.0
  instance HasCodec Double where
      hasCodec = Toml.double
      {-# INLINE hasCodec #-}
  
  -- | @since 1.1.0.0
  instance HasCodec Float where
      hasCodec = Toml.float
      {-# INLINE hasCodec #-}
  
  -- | @since 1.1.0.0
  instance HasCodec Text where
      hasCodec = Toml.text
      {-# INLINE hasCodec #-}
  
  -- | @since 1.1.0.0
  instance HasCodec L.Text where
      hasCodec = Toml.lazyText
      {-# INLINE hasCodec #-}
  
  -- | @since 1.3.0.0
  instance HasCodec ByteStringAsText where
      hasCodec = diwrap . Toml.byteString
      {-# INLINE hasCodec #-}
  
  -- | @since 1.3.0.0
  instance HasCodec ByteStringAsBytes where
      hasCodec = diwrap . Toml.byteStringArray
      {-# INLINE hasCodec #-}
  
  -- | @since 1.3.0.0
  instance HasCodec LByteStringAsText where
      hasCodec = diwrap . Toml.lazyByteString
      {-# INLINE hasCodec #-}
  
  -- | @since 1.3.0.0
  instance HasCodec LByteStringAsBytes where
      hasCodec = diwrap . Toml.lazyByteStringArray
      {-# INLINE hasCodec #-}
  
  -- | @since 1.1.0.0
  instance HasCodec ZonedTime where
      hasCodec = Toml.zonedTime
      {-# INLINE hasCodec #-}
  
  -- | @since 1.1.0.0
  instance HasCodec LocalTime where
      hasCodec = Toml.localTime
      {-# INLINE hasCodec #-}
  
  -- | @since 1.1.0.0
  instance HasCodec Day where
      hasCodec = Toml.day
      {-# INLINE hasCodec #-}
  
  -- | @since 1.1.0.0
  instance HasCodec TimeOfDay where
      hasCodec = Toml.timeOfDay
      {-# INLINE hasCodec #-}
  
  -- | @since 1.1.0.0
  instance HasCodec IntSet where
      hasCodec = Toml.arrayIntSet
      {-# INLINE hasCodec #-}
  
  -- | @since 1.1.0.0
  instance HasCodec a => HasCodec (Maybe a) where
      hasCodec = Toml.dioptional . hasCodec @a
      {-# INLINE hasCodec #-}
  
  -- | @since 1.1.0.0
  instance HasItemCodec a => HasCodec [a] where
      hasCodec = case hasItemCodec @a of
          Left prim   -> Toml.arrayOf prim
          Right codec -> Toml.list codec
      {-# INLINE hasCodec #-}
  
  -- | @since 1.1.0.0
  instance HasItemCodec a => HasCodec (NonEmpty a) where
      hasCodec = case hasItemCodec @a of
          Left prim   -> Toml.arrayNonEmptyOf prim
          Right codec -> Toml.nonEmpty codec
      {-# INLINE hasCodec #-}
  
  -- | @since 1.2.0.0
  instance (Ord a, HasItemCodec a) => HasCodec (Set a) where
      hasCodec = case hasItemCodec @a of
          Left prim   -> Toml.arraySetOf prim
          Right codec -> Toml.set codec
      {-# INLINE hasCodec #-}
  
  -- | @since 1.2.0.0
  #if MIN_VERSION_hashable(1,4,0)
  instance (Hashable a, HasItemCodec a)
  #else
  instance (Hashable a, Eq a, HasItemCodec a)
  #endif
    => HasCodec (HashSet a) where
      hasCodec = case hasItemCodec @a of
          Left prim   -> Toml.arrayHashSetOf prim
          Right codec -> Toml.hashSet codec
      {-# INLINE hasCodec #-}
  
  {- | Encodes 'Map' as array of tables with the @key@ and @val@ TOML
  key names for 'Map' keys and values. E.g. if you have a type
  @'Map' 'Int' 'Text'@, the 'HasCodec' instance for 'Generic' deriving
  will work with the following TOML representation:
  
  @
  fieldName =
      [ { key = 10, val = "book" }
      , { key = 42, val = "food" }
      ]
  @
  
  @since 1.3.0.0
  -}
  instance (Ord k, HasCodec k, HasCodec v) => HasCodec (Map k v) where
      hasCodec = Toml.map (hasCodec @k "key") (hasCodec @v "val")
      {-# INLINE hasCodec #-}
  
  {- | Encodes 'HashMap' as array of tables with the @key@ and @val@ TOML
  key names for 'HashMap' keys and values. E.g. if you have a type
  @'HashMap' 'Text' 'Int'@, the 'HasCodec' instance for 'Generic'
  deriving will work with the following TOML representation:
  
  @
  fieldName =
      [ { key = "foo", val = 15 }
      , { key = "bar", val = 7  }
      ]
  @
  
  @since 1.3.0.0
  -}
  #if MIN_VERSION_hashable(1,4,0)
  instance (Hashable k, HasCodec k, HasCodec v)
  #else
  instance (Hashable k, Eq k, HasCodec k, HasCodec v)
  #endif
    => HasCodec (HashMap k v) where
      hasCodec = Toml.hashMap (hasCodec @k "key") (hasCodec @v "val")
      {-# INLINE hasCodec #-}
  
  {- | Encodes 'IntMap' as array of tables with the @key@ and @val@ TOML
  key names for 'IntMap' keys and values. E.g. if you have a type
  @'IntMap' 'Text'@, the 'HasCodec' instance for 'Generic' deriving will
  work with the following TOML representation:
  
  @
  fieldName =
      [ { key = 10, val = "foo" }
      , { key = 42, val = "bar" }
      ]
  @
  
  @since 1.3.0.0
  -}
  instance (HasCodec v) => HasCodec (IntMap v) where
      hasCodec = Toml.intMap (hasCodec @Int "key") (hasCodec @v "val")
      {-# INLINE hasCodec #-}
  
  -- | @since 1.3.0.0
  instance HasCodec All where
      hasCodec = Toml.all
      {-# INLINE hasCodec #-}
  
  -- | @since 1.3.0.0
  instance HasCodec Any where
      hasCodec = Toml.any
      {-# INLINE hasCodec #-}
  
  -- | @since 1.3.0.0
  instance (Num a, HasCodec a) => HasCodec (Sum a) where
      hasCodec = Toml.sum (hasCodec @a)
      {-# INLINE hasCodec #-}
  
  -- | @since 1.3.0.0
  instance (Num a, HasCodec a) => HasCodec (Product a) where
      hasCodec = Toml.product (hasCodec @a)
      {-# INLINE hasCodec #-}
  
  -- | @since 1.3.0.0
  instance HasCodec a => HasCodec (First a) where
      hasCodec = Toml.first (hasCodec @a)
      {-# INLINE hasCodec #-}
  
  -- | @since 1.3.0.0
  instance HasCodec a => HasCodec (Last a) where
      hasCodec = Toml.last (hasCodec @a)
      {-# INLINE hasCodec #-}
  
  {- | @newtype@ for generic deriving of 'HasCodec' typeclass for custom data
  types that should we wrapped into separate table. Use it only for data types
  that are fields of another data types.
  
  @
  __data__ Person = Person
      { personName    :: !'Text'
      , personAddress :: !Address
      } __deriving__ ('Generic')
  
  data Address = Address
      { addressStreet :: !'Text'
      , addressHouse  :: !'Int'
      } __deriving__ ('Generic')
        __deriving__ 'HasCodec' __via__ 'TomlTable' Address
  
  personCodec :: 'TomlCodec' Person
  personCodec = 'stripTypeNameCodec'
  @
  
  @personCodec@ corresponds to the TOML of the following structure:
  
  @
  name = "foo"
  [address]
      addressStreet = \"Bar\"
      addressHouse = 42
  @
  
  @since 1.3.0.0
  -}
  newtype TomlTable a = TomlTable
      { unTomlTable :: a
      }
  
  -- | @since 1.3.0.0
  instance (Generic a, GenericCodec (Rep a)) => HasCodec (TomlTable a) where
      hasCodec :: Key -> TomlCodec (TomlTable a)
      hasCodec = Toml.diwrap . Toml.table (genericCodec @a)
      {-# INLINE hasCodec #-}
  
  -- | @since 1.3.0.0
  instance (Generic a, GenericCodec (Rep a)) => HasItemCodec (TomlTable a) where
      hasItemCodec = Right $ Toml.diwrap $ genericCodec @a
      {-# INLINE hasItemCodec #-}
  
  {- | @newtype@ for generic deriving of 'HasCodec' typeclass for custom data
  types that should be wrapped into a separate table.
  
  Similar to 'TomlTable' but also strips the data type name prefix from
  TOML keys.
  
  @personCodec@ from the 'TomlTable' comment corresponds to the TOML of
  the following structure:
  
  @
  name = "foo"
  [address]
      street = \"Bar\"
      house = 42
  @
  
  @since 1.3.2.0
  -}
  newtype TomlTableStrip a = TomlTableStrip
      { unTomlTableStrip :: a
      }
  
  -- | @since 1.3.2.0
  instance (Generic a, GenericCodec (Rep a), Typeable a) => HasCodec (TomlTableStrip a) where
      hasCodec :: Key -> TomlCodec (TomlTableStrip a)
      hasCodec = Toml.diwrap . Toml.table (stripTypeNameCodec @a)
      {-# INLINE hasCodec #-}
  
  -- | @since 1.3.2.0
  instance (Generic a, GenericCodec (Rep a), Typeable a) => HasItemCodec (TomlTableStrip a) where
      hasItemCodec = Right $ Toml.diwrap $ stripTypeNameCodec @a
      {-# INLINE hasItemCodec #-}
  
  {- $bytestring
  There are two ways to encode 'ByteString' in TOML:
  
  1. Via text.
  2. Via an array of integers (aka array of bytes).
  
  To handle all these cases, @tomland@ provides helpful newtypes, specifically:
  
  * 'ByteStringAsText'
  * 'ByteStringAsBytes'
  * 'LByteStringAsText'
  * 'LByteStringAsBytes'
  
  As a bonus, on GHC >= 8.6 you can use these newtypes with the @DerivingVia@
  extensions for your own 'ByteString' types.
  
  @
  __newtype__ MyByteString = MyByteString
      { unMyByteString :: 'ByteString'
      } __deriving__ 'HasCodec' __via__ 'ByteStringAsBytes'
  @
  -}
  
  {- | Newtype wrapper over 'ByteString' to be used for text values.
  
  @since 1.3.0.0
  -}
  newtype ByteStringAsText = ByteStringAsText
      { unByteStringAsText :: ByteString
      } deriving newtype (Show, Eq)
  
  {- | Newtype wrapper over 'ByteString' to be used for array of integers
  representation.
  
  @since 1.3.0.0
  -}
  newtype ByteStringAsBytes = ByteStringAsBytes
      { unByteStringAsBytes :: ByteString
      } deriving newtype (Show, Eq)
  
  {- | Newtype wrapper over lazy 'LBS.ByteString' to be used for text values.
  
  @since 1.3.0.0
  -}
  newtype LByteStringAsText = LByteStringAsText
      { unLByteStringAsText :: LBS.ByteString
      } deriving newtype (Show, Eq)
  
  {- | Newtype wrapper over lazy 'LBS.ByteString' to be used for array of integers
  representation.
  
  @since 1.3.0.0
  -}
  newtype LByteStringAsBytes = LByteStringAsBytes
      { unLByteStringAsBytes :: LBS.ByteString
      } deriving newtype (Show, Eq)
  