{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeFamilies #-}
module Waargonaut.Encode
(
Encoder (Encoder)
, Encoder'
, encodeA
, encodePureA
, runPureEncoder
, runEncoder
, simpleEncodeNoSpaces
, simplePureEncodeNoSpaces
, int
, scientific
, bool
, text
, null
, either
, maybe
, maybeOrNull
, traversable
, list
, nonempty
, mapToObj
, json
, prismE
, mapLikeObj
, atKey
, intAt
, textAt
, boolAt
, traversableAt
, listAt
, nonemptyAt
, encAt
, keyValuesAsObj
, onObj
, keyValueTupleFoldable
, int'
, scientific'
, bool'
, text'
, null'
, either'
, maybe'
, maybeOrNull'
, traversable'
, nonempty'
, list'
, atKey'
, mapLikeObj'
, mapToObj'
, keyValuesAsObj'
, json'
, generaliseEncoder'
) where
import Control.Monad.Morph (MFunctor (..), generalize)
import Control.Applicative (Applicative (..), (<$>))
import Control.Category (id, (.))
import Control.Lens (AReview, At, Index, IxValue,
Prism', Rewrapped, Wrapped (..),
at, cons, iso, ( # ), (?~), _Empty,
_Wrapped)
import qualified Control.Lens as L
import Prelude (Bool, Int, Monad)
import Data.Foldable (Foldable, foldr, foldrM)
import Data.Function (const, flip, ($), (&))
import Data.Functor (Functor, fmap)
import Data.Functor.Contravariant (Contravariant (..), (>$<))
import Data.Functor.Identity (Identity (..))
import Data.Traversable (Traversable, traverse)
import Data.Either (Either)
import qualified Data.Either as Either
import Data.List.NonEmpty (NonEmpty)
import Data.Maybe (Maybe)
import qualified Data.Maybe as Maybe
import Data.Scientific (Scientific)
import Data.Monoid (Monoid, mempty)
import Data.Semigroup (Semigroup)
import qualified Data.ByteString.Builder as BB
import Data.ByteString.Lazy (ByteString)
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Text (Text)
import Waargonaut.Types (AsJType (..), JAssoc (..), JObject,
Json, MapLikeObj (..), WS,
textToJString, wsRemover,
_JNumberInt, _JNumberScientific)
import Waargonaut.Types.Json (waargonautBuilder)
newtype Encoder f a = Encoder
{ runEncoder :: a -> f Json
}
instance (Encoder f a) ~ t => Rewrapped (Encoder f a) t
instance Wrapped (Encoder f a) where
type Unwrapped (Encoder f a) = a -> f Json
_Wrapped' = iso runEncoder Encoder
instance Contravariant (Encoder f) where
contramap f (Encoder g) = Encoder (g . f)
instance MFunctor Encoder where
hoist nat (Encoder eFn) = Encoder (nat . eFn)
generaliseEncoder' :: Monad f => Encoder' a -> Encoder f a
generaliseEncoder' = Encoder . fmap generalize . runEncoder
{-# INLINE generaliseEncoder' #-}
type Encoder' = Encoder Identity
encodeA :: (a -> f Json) -> Encoder f a
encodeA = Encoder
encodePureA :: (a -> Json) -> Encoder' a
encodePureA f = encodeA (Identity . f)
runPureEncoder :: Encoder' a -> a -> Json
runPureEncoder enc = runIdentity . runEncoder enc
simpleEncodeNoSpaces
:: Applicative f
=> Encoder f a
-> a
-> f ByteString
simpleEncodeNoSpaces enc =
fmap (BB.toLazyByteString . waargonautBuilder wsRemover) . runEncoder enc
simplePureEncodeNoSpaces
:: Encoder' a
-> a
-> ByteString
simplePureEncodeNoSpaces enc =
runIdentity . simpleEncodeNoSpaces enc
json :: Applicative f => Encoder f Json
json = encodeA pure
encToJsonNoSpaces
:: ( Monoid t
, Applicative f
)
=> AReview Json (b, t)
-> (a -> b)
-> Encoder f a
encToJsonNoSpaces c f =
encodeA (pure . (c #) . (,mempty) . f)
prismE
:: Prism' a b
-> Encoder f a
-> Encoder f b
prismE p e =
L.review p >$< e
int :: Applicative f => Encoder f Int
int = encToJsonNoSpaces _JNum (_JNumberInt #)
scientific :: Applicative f => Encoder f Scientific
scientific = encToJsonNoSpaces _JNum (_JNumberScientific #)
bool :: Applicative f => Encoder f Bool
bool = encToJsonNoSpaces _JBool id
text :: Applicative f => Encoder f Text
text = encToJsonNoSpaces _JStr textToJString
null :: Applicative f => Encoder f ()
null = encodeA $ const (pure $ _JNull # mempty)
maybe
:: Encoder f ()
-> Encoder f a
-> Encoder f (Maybe a)
maybe encN = encodeA
. Maybe.maybe (runEncoder encN ())
. runEncoder
maybeOrNull
:: Applicative f
=> Encoder f a
-> Encoder f (Maybe a)
maybeOrNull =
maybe null
either
:: Encoder f a
-> Encoder f b
-> Encoder f (Either a b)
either eA = encodeA
. Either.either (runEncoder eA)
. runEncoder
traversable
:: ( Applicative f
, Traversable t
)
=> Encoder f a
-> Encoder f (t a)
traversable = encodeWithInner
(\xs -> _JArr # (_Wrapped # foldr cons mempty xs, mempty))
mapToObj
:: Applicative f
=> Encoder f a
-> (k -> Text)
-> Encoder f (Map k a)
mapToObj encodeVal kToText =
let
mapToCS = Map.foldrWithKey (\k v -> at (kToText k) ?~ v) (_Empty # ())
in
encodeWithInner (\xs -> _JObj # (fromMapLikeObj $ mapToCS xs, mempty)) encodeVal
nonempty
:: Applicative f
=> Encoder f a
-> Encoder f (NonEmpty a)
nonempty =
traversable
list
:: Applicative f
=> Encoder f a
-> Encoder f [a]
list =
traversable
json' :: Encoder' Json
json' = json
int' :: Encoder' Int
int' = int
scientific' :: Encoder' Scientific
scientific' = scientific
bool' :: Encoder' Bool
bool' = bool
text' :: Encoder' Text
text' = text
null' :: Encoder' ()
null' = null
maybe'
:: Encoder' ()
-> Encoder' a
-> Encoder' (Maybe a)
maybe' =
maybe
maybeOrNull'
:: Encoder' a
-> Encoder' (Maybe a)
maybeOrNull' =
maybeOrNull
either'
:: Encoder' a
-> Encoder' b
-> Encoder' (Either a b)
either' =
either
nonempty'
:: Encoder' a
-> Encoder' (NonEmpty a)
nonempty' =
traversable
list'
:: Encoder' a
-> Encoder' [a]
list' =
traversable
encodeWithInner
:: ( Applicative f
, Traversable t
)
=> (t Json -> Json)
-> Encoder f a
-> Encoder f (t a)
encodeWithInner f g =
Encoder $ fmap f . traverse (runEncoder g)
traversable'
:: Traversable t
=> Encoder' a
-> Encoder' (t a)
traversable' =
traversable
mapToObj'
:: Encoder' a
-> (k -> Text)
-> Encoder' (Map k a)
mapToObj' =
mapToObj
atKey
:: ( At t
, IxValue t ~ Json
, Applicative f
)
=> Index t
-> Encoder f a
-> a
-> t
-> f t
atKey k enc v t =
(\v' -> t & at k ?~ v') <$> runEncoder enc v
atKey'
:: ( At t
, IxValue t ~ Json
)
=> Index t
-> Encoder' a
-> a
-> t
-> t
atKey' k enc v =
at k ?~ runIdentity (runEncoder enc v)
intAt
:: Text
-> Int
-> MapLikeObj WS Json
-> MapLikeObj WS Json
intAt =
flip atKey' int
textAt
:: Text
-> Text
-> MapLikeObj WS Json
-> MapLikeObj WS Json
textAt =
flip atKey' text
boolAt
:: Text
-> Bool
-> MapLikeObj WS Json
-> MapLikeObj WS Json
boolAt =
flip atKey' bool
traversableAt
:: ( At t
, Traversable f
, IxValue t ~ Json
)
=> Encoder' a
-> Index t
-> f a
-> t
-> t
traversableAt enc =
flip atKey' (traversable enc)
listAt
:: ( At t
, IxValue t ~ Json
)
=> Encoder' a
-> Index t
-> [a]
-> t
-> t
listAt =
traversableAt
nonemptyAt
:: ( At t
, IxValue t ~ Json
)
=> Encoder' a
-> Index t
-> NonEmpty a
-> t
-> t
nonemptyAt =
traversableAt
mapLikeObj
:: ( AsJType Json ws a
, Monoid ws
, Semigroup ws
, Applicative f
)
=> (i -> MapLikeObj ws a -> MapLikeObj ws a)
-> Encoder f i
mapLikeObj f = encodeA $ \a ->
pure $ _JObj # (fromMapLikeObj $ f a (_Empty # ()), mempty)
mapLikeObj'
:: ( AsJType Json ws a
, Semigroup ws
, Monoid ws
)
=> (i -> MapLikeObj ws a -> MapLikeObj ws a)
-> Encoder' i
mapLikeObj' f = encodePureA $ \a ->
_JObj # (fromMapLikeObj $ f a (_Empty # ()), mempty)
onObj
:: Applicative f
=> Text
-> b
-> Encoder f b
-> JObject WS Json
-> f (JObject WS Json)
onObj k b encB o = (\j -> o & _Wrapped L.%~ L.cons j)
. JAssoc (textToJString k) mempty mempty <$> runEncoder encB b
keyValuesAsObj
:: ( Foldable g
, Monad f
)
=> g (a -> JObject WS Json -> f (JObject WS Json))
-> Encoder f a
keyValuesAsObj xs = encodeA $ \a ->
(\v -> _JObj # (v,mempty)) <$> foldrM (\f -> f a) (_Empty # ()) xs
keyValueTupleFoldable
:: ( Monad f
, Foldable g
)
=> Encoder f a
-> Encoder f (g (Text, a))
keyValueTupleFoldable eA = encodeA $ \xs ->
(\v -> _JObj # (v,mempty)) <$> foldrM (\(k,v) o -> onObj k v eA o) (_Empty # ()) xs
keyValuesAsObj'
:: ( Foldable g
, Functor g
)
=> g (a -> JObject WS Json -> JObject WS Json)
-> Encoder' a
keyValuesAsObj' =
keyValuesAsObj . fmap (\f a -> Identity . f a)
encAt
:: Applicative f
=> Encoder f b
-> Text
-> (a -> b)
-> a
-> JObject WS Json
-> f (JObject WS Json)
encAt e k f a =
onObj k (f a) e