{-# LANGUAGE FlexibleInstances, UndecidableInstances, ScopedTypeVariables #-}
{-|
Module      : Toml.Generic
Description : Integration with DerivingVia extension
Copyright   : (c) Eric Mertens, 2024
License     : ISC
Maintainer  : emertens@gmail.com

This module makes it possible to easily derive the TOML classes
using the @DerivingVia@ extension.

For example:

@
data Physical = Physical {
    color :: String,
    shape :: String
    }
    deriving (Eq, Show, Generic)
    deriving (ToTable, ToValue, FromValue) via GenericTomlTable Physical
@

These derived instances would allow you to match TOML @{color="red", shape="round"}@ to value @Coord 1 2@.

@
data Coord = Coord Int Int
    deriving (Eq, Show, Generic)
    deriving (ToValue, FromValue) via GenericTomlArray Physical
@

These derived instances would allow you to match TOML @[1,2]@ to value @Coord 1 2@.

-}
module Toml.Generic (
    GenericTomlTable(GenericTomlTable),
    GenericTomlArray(GenericTomlArray),
    ) where

import Data.Coerce (coerce)
import GHC.Generics (Generic(Rep))
import Toml.FromValue (FromValue(fromValue), parseTableFromValue)
import Toml.FromValue.Generic (GParseTable, GFromArray, genericParseTable, genericFromArray)
import Toml.FromValue.Matcher (Matcher)
import Toml.ToValue (ToTable(toTable), ToValue(toValue), defaultTableToValue)
import Toml.ToValue.Generic (GToTable, GToArray, genericToTable, genericToArray)
import Toml.Value (Value, Table)

-- | Helper type to use GHC's DerivingVia extension to derive
-- 'ToValue', 'ToTable', 'FromValue' for records.
--
-- @since 1.3.2.0
newtype GenericTomlTable a = GenericTomlTable a

-- | Instance derived from 'ToTable' instance using 'defaultTableToValue'
instance (Generic a, GToTable (Rep a)) => ToValue (GenericTomlTable a) where
    toValue :: GenericTomlTable a -> Value
toValue = GenericTomlTable a -> Value
forall a. ToTable a => a -> Value
defaultTableToValue
    {-# INLINE toValue #-}

-- | Instance derived using 'genericToTable'
instance (Generic a, GToTable (Rep a)) => ToTable (GenericTomlTable a) where
    toTable :: GenericTomlTable a -> Table
toTable = (a -> Table) -> GenericTomlTable a -> Table
forall a b. Coercible a b => a -> b
coerce (a -> Table
forall a. (Generic a, GToTable (Rep a)) => a -> Table
genericToTable :: a -> Table)
    {-# INLINE toTable #-}

-- | Instance derived using 'genericParseTable'
instance (Generic a, GParseTable (Rep a)) => FromValue (GenericTomlTable a) where
    fromValue :: Value -> Matcher (GenericTomlTable a)
fromValue = (Value -> Matcher a) -> Value -> Matcher (GenericTomlTable a)
forall a b. Coercible a b => a -> b
coerce (ParseTable a -> Value -> Matcher a
forall a. ParseTable a -> Value -> Matcher a
parseTableFromValue ParseTable a
forall a. (Generic a, GParseTable (Rep a)) => ParseTable a
genericParseTable :: Value -> Matcher a)
    {-# INLINE fromValue #-}

-- | Helper type to use GHC's DerivingVia extension to derive
-- 'ToValue', 'ToTable', 'FromValue' for any product type.
--
-- @since 1.3.2.0
newtype GenericTomlArray a = GenericTomlArray a

-- | Instance derived using 'genericToArray'
instance (Generic a, GToArray (Rep a)) => ToValue (GenericTomlArray a) where
    toValue :: GenericTomlArray a -> Value
toValue = (a -> Value) -> GenericTomlArray a -> Value
forall a b. Coercible a b => a -> b
coerce (a -> Value
forall a. (Generic a, GToArray (Rep a)) => a -> Value
genericToArray :: a -> Value)
    {-# INLINE toValue #-}

-- | Instance derived using 'genericFromArray'
instance (Generic a, GFromArray (Rep a)) => FromValue (GenericTomlArray a) where
    fromValue :: Value -> Matcher (GenericTomlArray a)
fromValue = (Value -> Matcher a) -> Value -> Matcher (GenericTomlArray a)
forall a b. Coercible a b => a -> b
coerce (Value -> Matcher a
forall a. (Generic a, GFromArray (Rep a)) => Value -> Matcher a
genericFromArray :: Value -> Matcher a)
    {-# INLINE fromValue #-}