-- | This module exports facilities similar to those exported by the
-- "Control.Proxy.Aeson" module, except they do not restrict the 'Ae.Value's
-- that might be encoded or decoded to be just valid top-level values. That is,
-- not only 'Ae.Object's or 'Ae.Array's, according to to the RFC-4627 JSON
-- standard.

module Control.Proxy.Aeson.Unsafe
  (-- * Encoding
    -- $encoding
    encode
  , encodeD
    -- * Decoding
    -- $decoding
  , decode
  , decodeD
    -- ** Lower level parsing
  , parseValue
  , parseValueD
    -- * Types
  , I.DecodingError(..)
  ) where

import           Control.Monad                 (unless)
import qualified Control.Proxy                 as P
import qualified Control.Proxy.Aeson.Internal  as I
import qualified Control.Proxy.Attoparsec      as PA
import qualified Control.Proxy.Trans.Either    as P
import qualified Control.Proxy.Trans.State     as P
import qualified Data.Aeson                    as Ae
import qualified Data.Aeson.Parser             as Ae (value')
import qualified Data.ByteString.Char8         as B

--------------------------------------------------------------------------------

-- | Like 'Control.Proxy.Aeson.encode', except it accepts any 'Ae.ToJSON'
-- instance.
encode
  :: (P.Proxy p, Monad m, Ae.ToJSON a) => a -> p x' x () B.ByteString m ()
encode = I.fromLazy . Ae.encode
{-# INLINABLE encode #-}

-- | Like 'Control.Proxy.Aeson.encodeD', except it accepts any 'Ae.ToJSON'
-- instance.
encodeD
  :: (P.Proxy p, Monad m, Ae.ToJSON a) => () -> P.Pipe p a B.ByteString m r
encodeD = P.pull P./>/ encode
{-# INLINABLE encodeD #-}

--------------------------------------------------------------------------------

-- | Like 'Control.Proxy.Aeson.decode', except it will decode any 'Ae.ToJSON'
-- instance.
decode
  :: (Monad m, P.Proxy p, Ae.FromJSON r)
  => P.EitherP I.DecodingError (P.StateP [B.ByteString] p)
     () (Maybe B.ByteString) y' y m r
decode = do
    ev <- P.liftP . P.runEitherP $ PA.parse Ae.value'
    case ev of
      Left e  -> P.throw (I.ParserError e)
      Right v ->
        case Ae.fromJSON v of
          Ae.Error e   -> P.throw (I.ValueError e)
          Ae.Success r -> return r
{-# INLINABLE decode #-}

-- | Like 'Control.Proxy.Aeson.decodeD', except it will decode any 'Ae.ToJSON'
-- instance.
decodeD
  :: (Monad m, P.Proxy p, Ae.FromJSON b)
  => ()
  -> P.Pipe (P.EitherP I.DecodingError (P.StateP [B.ByteString] p))
     (Maybe B.ByteString) b m ()
decodeD = \() -> loop where
    loop = do
        eof <- P.liftP $ I.skipSpace >> PA.isEndOfParserInput
        unless eof $ decode >>= P.respond >> loop
{-# INLINABLE decodeD #-}

--------------------------------------------------------------------------------

-- | Like 'Control.Proxy.Aeson.parseValue', except it will parse into any
-- 'Ae.Value'.
parseValue
  :: (Monad m, P.Proxy p)
  => P.EitherP PA.ParsingError (P.StateP [B.ByteString] p)
     () (Maybe B.ByteString) y' y m Ae.Value
parseValue = PA.parse Ae.value'
{-# INLINABLE parseValue #-}

-- | Like 'Control.Proxy.Aeson.parseValueD', except it will parse into any
-- 'Ae.Value'.
parseValueD
  :: (Monad m, P.Proxy p)
  => ()
  -> P.Pipe (P.EitherP PA.ParsingError (P.StateP [B.ByteString] p))
     (Maybe B.ByteString) Ae.Value m ()
parseValueD = \() -> loop where
    loop = do
        eof <- P.liftP $ I.skipSpace >> PA.isEndOfParserInput
        unless eof $ parseValue >>= P.respond >> loop
{-# INLINABLE parseValueD #-}