{- |
Copyright: (c) 2018-2020 Kowainik
SPDX-License-Identifier: MPL-2.0
Maintainer: Kowainik <xrom.xkov@gmail.com>

TOML-specific combinators for converting between TOML and Haskell Set-like data
types.

There are two way to represent list-like structures with the @tomland@ library.

* Ordinary array sets of primitives:

    @
    foo = [100, 200, 300]
    @

* Sets via tables:

    @
    foo =
        [ {x = 100}
        , {x = 200}
        , {x = 300}
        ]

    __OR__

    [[foo]]
        x = 100
    [[foo]]
        x = 200
    [[foo]]
        x = 300
    @

You can find both types of the codecs in this module for different set-like
structures. See the following table for the better understanding:

+------------------------+----------------------------------+-------------------------------------+
|      Haskell Type      |              @TOML@              |             'TomlCodec'             |
+========================+==================================+=====================================+
| __@'Set' 'Text'@__     | @a = ["foo", "bar", "baz"]@      | @'arraySetOf' 'Toml._Text' "a"@     |
+------------------------+----------------------------------+-------------------------------------+
| __'IntSet'__           | @a = [11, 42]@                   | @'arrayIntSet' "a"@                 |
+------------------------+----------------------------------+-------------------------------------+
| __@'HashSet' 'Text'@__ | @a = ["foo", "bar"]@             | @'arrayHashSetOf' 'Toml._Text' "a"@ |
+------------------------+----------------------------------+-------------------------------------+
| __@'Set' 'Text'@__     | @x = [{a = "foo"}, {a = "bar"}]@ | @'set' ('Toml.text' "a") "x"@       |
+------------------------+----------------------------------+-------------------------------------+
| __@'HashSet' 'Text'@__ | @x = [{a = "foo"}, {a = "bar"}]@ | @'hashSet' ('Toml.text' "a") "x"@   |
+------------------------+----------------------------------+-------------------------------------+

@since 1.3.0.0
-}

module Toml.Codec.Combinator.Set
    ( -- * Array sets
      arraySetOf
    , arrayIntSet
    , arrayHashSetOf
      -- * Table Sets
    , set
    , intSet
    , hashSet
    ) where

import Data.Hashable (Hashable)
import Data.HashSet (HashSet)
import Data.IntSet (IntSet)
import Data.Set (Set)

import Toml.Codec.BiMap (TomlBiMap)
import Toml.Codec.BiMap.Conversion (_HashSet, _IntSet, _Set)
import Toml.Codec.Combinator.Common (match)
import Toml.Codec.Combinator.List (list)
import Toml.Codec.Di (dimap)
import Toml.Codec.Types (TomlCodec)
import Toml.Type.AnyValue (AnyValue (..))
import Toml.Type.Key (Key)

import qualified Data.HashSet as HS
import qualified Data.IntSet as IS
import qualified Data.Set as S


{- | Codec for sets. Takes converter for single value and
returns a set of values.

__Example:__

Haskell @'Set' 'Int'@ can look like this in your @TOML@ file:

@
foo = [1, 2, 3]
@

In case of the missing field, the following error will be seen:

@
tomland decode error:  Key foo is not found
@

@since 0.5.0
-}
arraySetOf :: Ord a => TomlBiMap a AnyValue -> Key -> TomlCodec (Set a)
arraySetOf :: TomlBiMap a AnyValue -> Key -> TomlCodec (Set a)
arraySetOf = TomlBiMap (Set a) AnyValue -> Key -> TomlCodec (Set a)
forall a. TomlBiMap a AnyValue -> Key -> TomlCodec a
match (TomlBiMap (Set a) AnyValue -> Key -> TomlCodec (Set a))
-> (TomlBiMap a AnyValue -> TomlBiMap (Set a) AnyValue)
-> TomlBiMap a AnyValue
-> Key
-> TomlCodec (Set a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TomlBiMap a AnyValue -> TomlBiMap (Set a) AnyValue
forall a.
Ord a =>
TomlBiMap a AnyValue -> TomlBiMap (Set a) AnyValue
_Set
{-# INLINE arraySetOf #-}

{- | Codec for sets of ints. Takes converter for single value and
returns a set of ints.

__Example:__

Haskell @'IntSet'@ can look like this in your @TOML@ file:

@
foo = [1, 2, 3]
@

In case of the missing field, the following error will be seen:

@
tomland decode error:  Key foo is not found
@

@since 0.5.0
-}
arrayIntSet :: Key -> TomlCodec IntSet
arrayIntSet :: Key -> TomlCodec IntSet
arrayIntSet = TomlBiMap IntSet AnyValue -> Key -> TomlCodec IntSet
forall a. TomlBiMap a AnyValue -> Key -> TomlCodec a
match TomlBiMap IntSet AnyValue
_IntSet
{-# INLINE arrayIntSet #-}

{- | Codec for hash sets. Takes converter for single hashable value and
returns a set of hashable values.

__Example:__

Haskell @'HashSet' 'Int'@ can look like this in your @TOML@ file:

@
foo = [1, 2, 3]
@

In case of the missing field, the following error will be seen:

@
tomland decode error:  Key foo is not found
@

@since 0.5.0
-}
arrayHashSetOf
    :: (Hashable a, Eq a)
    => TomlBiMap a AnyValue
    -> Key
    -> TomlCodec (HashSet a)
arrayHashSetOf :: TomlBiMap a AnyValue -> Key -> TomlCodec (HashSet a)
arrayHashSetOf = TomlBiMap (HashSet a) AnyValue -> Key -> TomlCodec (HashSet a)
forall a. TomlBiMap a AnyValue -> Key -> TomlCodec a
match (TomlBiMap (HashSet a) AnyValue -> Key -> TomlCodec (HashSet a))
-> (TomlBiMap a AnyValue -> TomlBiMap (HashSet a) AnyValue)
-> TomlBiMap a AnyValue
-> Key
-> TomlCodec (HashSet a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TomlBiMap a AnyValue -> TomlBiMap (HashSet a) AnyValue
forall a.
(Eq a, Hashable a) =>
TomlBiMap a AnyValue -> TomlBiMap (HashSet a) AnyValue
_HashSet
{-# INLINE arrayHashSetOf #-}

----------------------------------------------------------------------------
-- Tables and arrays of tables
----------------------------------------------------------------------------

{- | 'Codec' for set of values. Represented in TOML as array of tables.

__Example:__

Haskell @'Set' 'Int'@ can look like this in your @TOML@ file:

@
foo =
  [ {a = 1}
  , {a = 2}
  ]
@

Decodes to an empty 'Set' in case of the missing field in @TOML@.

@since 1.2.0.0
-}
set :: forall a . Ord a => TomlCodec a -> Key -> TomlCodec (Set a)
set :: TomlCodec a -> Key -> TomlCodec (Set a)
set codec :: TomlCodec a
codec key :: Key
key = (Set a -> [a])
-> ([a] -> Set a) -> TomlCodec [a] -> TomlCodec (Set a)
forall b a. (b -> a) -> (a -> b) -> TomlCodec a -> TomlCodec b
dimap Set a -> [a]
forall a. Set a -> [a]
S.toList [a] -> Set a
forall a. Ord a => [a] -> Set a
S.fromList (TomlCodec a -> Key -> TomlCodec [a]
forall a. TomlCodec a -> Key -> TomlCodec [a]
list TomlCodec a
codec Key
key)
{-# INLINE set #-}

{- | 'Codec' for 'IntSet'. Represented in TOML as an array of tables.

__Example:__

Haskell 'IntSet' can look like this in your @TOML@ file:

@
foo =
  [ {a = 1}
  , {a = 2}
  ]
@

Decodes to an empty 'IntSet' in case of the missing field in @TOML@.

@since 1.3.0.0
-}
intSet :: TomlCodec Int -> Key -> TomlCodec IntSet
intSet :: TomlCodec Int -> Key -> TomlCodec IntSet
intSet codec :: TomlCodec Int
codec key :: Key
key = (IntSet -> [Int])
-> ([Int] -> IntSet) -> TomlCodec [Int] -> TomlCodec IntSet
forall b a. (b -> a) -> (a -> b) -> TomlCodec a -> TomlCodec b
dimap IntSet -> [Int]
IS.toList [Int] -> IntSet
IS.fromList (TomlCodec Int -> Key -> TomlCodec [Int]
forall a. TomlCodec a -> Key -> TomlCodec [a]
list TomlCodec Int
codec Key
key)
{-# INLINE intSet #-}

{- | 'Codec' for 'HashSet' of values. Represented in TOML as an array of tables.

__Example:__

Haskell @'HashSet' 'Int'@ can look like this in your @TOML@ file:

@
foo =
  [ {a = 1}
  , {a = 2}
  ]
@

Decodes to an empty 'HashSet' in case of the missing field in @TOML@.

@since 1.2.0.0
-}
hashSet :: forall a . (Hashable a, Eq a) => TomlCodec a -> Key -> TomlCodec (HashSet a)
hashSet :: TomlCodec a -> Key -> TomlCodec (HashSet a)
hashSet codec :: TomlCodec a
codec key :: Key
key = (HashSet a -> [a])
-> ([a] -> HashSet a) -> TomlCodec [a] -> TomlCodec (HashSet a)
forall b a. (b -> a) -> (a -> b) -> TomlCodec a -> TomlCodec b
dimap HashSet a -> [a]
forall a. HashSet a -> [a]
HS.toList [a] -> HashSet a
forall a. (Eq a, Hashable a) => [a] -> HashSet a
HS.fromList (TomlCodec a -> Key -> TomlCodec [a]
forall a. TomlCodec a -> Key -> TomlCodec [a]
list TomlCodec a
codec Key
key)
{-# INLINE hashSet #-}