{-# LANGUAGE RankNTypes #-}
-----------------------------------------------------------------------------
-- |
-- Module      :  Data.Aeson.Extra.Merge
-- Copyright   :  (C) 2015-2016 Oleg Grenrus
-- License     :  BSD3
-- Maintainer  :  Oleg Grenrus <oleg.grenrus@iki.fi>
--
module Data.Aeson.Extra.Merge (
	merge,
	mergeA,
	ValueF(..),
	ObjectF,
	ArrayF,
	) where

import Prelude        ()
import Prelude.Compat

import Data.Aeson.Compat
import Data.Aeson.Extra.Foldable
import Data.Functor.Foldable (project, embed)

-- | Generic merge.
--
-- For example <https://lodash.com/docs#merge>:
--
-- @
-- lodashMerge :: Value -> Value -> Value
-- lodashMerge x y = merge lodashMergeAlg x y
-- 
-- lodashMergeAlg :: (a -> a -> a) -> ValueF a -> ValueF a -> ValueF a
-- lodashMergeAlg r a' b' = case (a', b') of
--     (ObjectF a, ObjectF b) -> ObjectF $ alignWith f a b
--     (ArrayF a,  ArrayF b)  -> ArrayF $ alignWith f a b
--     (_,         b)         -> b
--   where f (These x y) = r x y
--         f (This x)    = x
--         f (That x)    = x
-- @
--
-- /Since: aeson-extra-0.3.1.0/
merge :: (forall a. (a -> a -> a) -> ValueF a -> ValueF a -> ValueF a)
       -> Value -> Value -> Value
merge f a b = embed $ f (merge f) (project a) (project b)

-- | Generic merge, in arbitrary context.
--
-- /Since: aeson-extra-0.3.1.0/
mergeA :: Functor f
      => (forall a. (a -> a -> f a) -> ValueF a -> ValueF a -> f (ValueF a))
      -> Value -> Value -> f Value
mergeA f a b = embed <$> f (mergeA f) (project a) (project b)