content: {- |
  Module                  : Toml.Parser.Item
  Copyright               : (c) 2018-2022 Kowainik
  SPDX-License-Identifier : MPL-2.0
  Maintainer              : Kowainik <xrom.xkov@gmail.com>
  Stability               : Stable
  Portability             : Portable
  
  This module contains the definition of the 'TomlItem' data type which
  represents either key-value pair or table name. This data type serves the
  purpose to be the intermediate representation of parsing a TOML file which will
  be assembled to TOML AST later.
  
  @since 1.2.0.0
  -}
  
  module Toml.Parser.Item
         ( TomlItem (..)
         , Table (..)
         , setTableName
  
         , tomlP
         , keyValP
         ) where
  
  import Control.Applicative (liftA2, many)
  import Control.Applicative.Combinators.NonEmpty (sepEndBy1)
  import Control.Monad.Combinators (between, sepEndBy)
  import Data.Foldable (asum)
  import Data.List.NonEmpty (NonEmpty)
  
  import Toml.Parser.Core (Parser, eof, sc, text, try, (<?>))
  import Toml.Parser.Key (keyP, tableArrayNameP, tableNameP)
  import Toml.Parser.Value (anyValueP)
  import Toml.Type.AnyValue (AnyValue)
  import Toml.Type.Key (Key)
  
  
  {- | One item of a TOML file. It could be either:
  
  * A name of a table
  * A name of a table array
  * Key-value pair
  * Inline table
  * Inline array of tables
  
  Knowing a list of 'TomlItem's, it's possible to construct 'Toml.Type.TOML.TOML'
  from this information.
  -}
  data TomlItem
      = TableName !Key
      | TableArrayName !Key
      | KeyVal !Key !AnyValue
      | InlineTable !Key !Table
      | InlineTableArray !Key !(NonEmpty Table)
      deriving stock (Show, Eq)
  
  {- | Changes name of table to a new one. Works only for 'TableName' and
  'TableArrayName' constructors.
  -}
  setTableName :: Key -> TomlItem -> TomlItem
  setTableName new = \case
      TableName _ -> TableName new
      TableArrayName _ -> TableArrayName new
      item -> item
  
  {- | Table that contains only @key = val@ pairs.
  -}
  newtype Table = Table
      { unTable :: [(Key, AnyValue)]
      } deriving stock (Show)
        deriving newtype (Eq)
  
  ----------------------------------------------------------------------------
  -- Parser
  ----------------------------------------------------------------------------
  
  -- | Parser for inline tables.
  inlineTableP :: Parser Table
  inlineTableP =
      fmap Table
      $ between (text "{") (text "}")
      $ liftA2 (,) (keyP <* text "=") anyValueP `sepEndBy` text ","
  
  -- | Parser for inline arrays of tables.
  inlineTableArrayP :: Parser (NonEmpty Table)
  inlineTableArrayP = between (text "[") (text "]")
      $ inlineTableP `sepEndBy1` text ","
  
  -- | Parser for a single item in the TOML file.
  tomlItemP :: Parser TomlItem
  tomlItemP = asum
      [ TableName <$> try tableNameP <?> "table name"
      , TableArrayName <$> tableArrayNameP <?> "array of tables name"
      , keyValP
      ]
  
  {- | parser for @"key = val"@ pairs; can be one of three forms:
  
  1. key = { ... }
  2. key = [ {...}, {...}, ... ]
  3. key = ...
  -}
  keyValP :: Parser TomlItem
  keyValP = do
      key <- keyP <* text "="
      asum
          [ InlineTable key <$> inlineTableP <?> "inline table"
          , InlineTableArray key <$> try inlineTableArrayP <?> "inline array of tables"
          , KeyVal key <$> anyValueP <?> "key-value pair"
          ]
  
  -- | Parser for the full content of the .toml file.
  tomlP :: Parser [TomlItem]
  tomlP = sc *> many tomlItemP <* eof
  