content: {- |
  Module                  : Toml.Codec.Combinator.Table
  Copyright               : (c) 2018-2022 Kowainik
  SPDX-License-Identifier : MPL-2.0
  Maintainer              : Kowainik <xrom.xkov@gmail.com>
  Stability               : Stable
  Portability             : Portable
  
  Contains TOML-specific combinators for converting between TOML and user data
  types.
  
  Tables can be represented in @TOML@ in one of the following ways:
  
  @
  foo =
      { x = ...
      , y = ...
      , ...
      }
  @
  
  __Or__
  
  @
  [foo]
      x = ...
      y = ...
      ...
  @
  
  @since 1.3.0.0
  -}
  
  module Toml.Codec.Combinator.Table
      ( -- * Tables
        table
        -- * Error Helpers
      , handleTableErrors
      , mapTableErrors
      ) where
  
  import Control.Monad.State (gets, modify)
  import Data.Maybe (fromMaybe)
  import Validation (Validation (..))
  
  import Toml.Codec.Error (TomlDecodeError (..))
  import Toml.Codec.Types (Codec (..), TomlCodec, TomlEnv, TomlState (..))
  import Toml.Type.Key (Key)
  import Toml.Type.TOML (TOML (..), insertTable)
  
  import qualified Toml.Type.PrefixTree as Prefix
  
  
  
  {- | Maps errors in tables with 'mapTableErrors'
  
  @since 1.3.0.0
  -}
  handleTableErrors :: TomlCodec a -> Key -> TOML -> Validation [TomlDecodeError] a
  handleTableErrors codec key toml = case codecRead codec toml of
      Success res  -> Success res
      Failure errs -> Failure $ mapTableErrors key errs
  
  {- | Prepends given key to all errors that contain key. This function is used to
  give better error messages. So when error happens we know all pieces of table
  key, not only the last one.
  
  @since 0.2.0
  -}
  mapTableErrors :: Key -> [TomlDecodeError] -> [TomlDecodeError]
  mapTableErrors key = map (\case
      KeyNotFound name        -> KeyNotFound (key <> name)
      TableNotFound name      -> TableNotFound (key <> name)
      TableArrayNotFound name -> TableArrayNotFound (key <> name)
      e                       -> e
      )
  
  {- | Codec for tables. Use it when when you have nested objects.
  
  @since 0.2.0
  -}
  table :: forall a . TomlCodec a -> Key -> TomlCodec a
  table codec key = Codec input output
    where
      input :: TomlEnv a
      input = \t -> case Prefix.lookup key $ tomlTables t of
          Nothing   -> Failure [TableNotFound key]
          Just toml -> handleTableErrors codec key toml
  
      output :: a -> TomlState a
      output a = do
          mTable <- gets $ Prefix.lookup key . tomlTables
          let toml = fromMaybe mempty mTable
          let (_, newToml) = unTomlState (codecWrite codec a) toml
          a <$ modify (insertTable key newToml)
  