-- | This module provides a few small functions to make working with
-- aeson easier. Hopefully at some point they won't be needed anymore.
module Data.Aeson.Utils
  ( module Data.Aeson
  , module Data.Aeson.Types
  -- * Parsing values
  , decodeV
  , eitherDecodeV
  -- * Utilities
  , fromFloatDigits
  , (.=?)
  , parseNumber
  , floatingOrInteger
  ) where

import Control.Applicative ((<*))
import Data.Aeson
import Data.Aeson.Parser (value)
import Data.Aeson.Types
import Data.Attoparsec.Lazy (Result (..))
import Data.Scientific
import Data.Text (Text)
import qualified Data.Attoparsec.ByteString.Char8 as Atto (skipSpace)
import qualified Data.Attoparsec.Lazy as Atto
import qualified Data.ByteString.Lazy as L

-- * Parsing values

-- | Like 'decodeV', but returns an error message when decoding fails.
eitherDecodeV :: FromJSON a => L.ByteString -> Either String a
eitherDecodeV v = case Atto.parse (value <* Atto.skipSpace <* Atto.endOfInput) v of
  Fail _ _ err -> Left err
  Done _ r     -> case fromJSON r of
    Error e   -> Left e
    Success a -> Right a

-- | Deserialize any JSON value. Allows atomic values on the top level
decodeV :: FromJSON a => L.ByteString -> Maybe a
decodeV = either (const Nothing) Just . eitherDecodeV

-- * Utilities

-- | Optionally create a Pair.
(.=?) :: ToJSON a => Text -> Maybe a -> Maybe Pair
k .=? v = fmap (k .=) v
{-# INLINE (.=?) #-}

-- | Convert a Scientific into an Integer if it doesn't have decimal points,
-- otherwise to a Double.
parseNumber :: Scientific -> Either Integer Double
parseNumber = either Right Left . floatingOrInteger
{-# DEPRECATED parseNumber "Use Data.Scientific.floatingOrInteger instead" #-}