dhodovsk / source-git / ghc-aeson

Forked from source-git/ghc-aeson 4 years ago
Clone

Blame Data/Aeson/Encoding/Internal.hs

Packit 9a2dfb
{-# LANGUAGE DeriveDataTypeable #-}
Packit 9a2dfb
{-# LANGUAGE EmptyDataDecls #-}
Packit 9a2dfb
{-# LANGUAGE OverloadedStrings #-}
Packit 9a2dfb
{-# LANGUAGE RankNTypes #-}
Packit 9a2dfb
module Data.Aeson.Encoding.Internal
Packit 9a2dfb
    (
Packit 9a2dfb
    -- * Encoding
Packit 9a2dfb
      Encoding' (..)
Packit 9a2dfb
    , Encoding
Packit 9a2dfb
    , encodingToLazyByteString
Packit 9a2dfb
    , unsafeToEncoding
Packit 9a2dfb
    , retagEncoding
Packit 9a2dfb
    , Series (..)
Packit 9a2dfb
    , pairs
Packit 9a2dfb
    , pair
Packit 9a2dfb
    , pairStr
Packit 9a2dfb
    , pair'
Packit 9a2dfb
    -- * Predicates
Packit 9a2dfb
    , nullEncoding
Packit 9a2dfb
    -- * Encoding constructors
Packit 9a2dfb
    , emptyArray_
Packit 9a2dfb
    , emptyObject_
Packit 9a2dfb
    , wrapObject
Packit 9a2dfb
    , wrapArray
Packit 9a2dfb
    , null_
Packit 9a2dfb
    , bool
Packit 9a2dfb
    , text
Packit 9a2dfb
    , lazyText
Packit 9a2dfb
    , string
Packit 9a2dfb
    , list
Packit 9a2dfb
    , dict
Packit 9a2dfb
    , tuple
Packit 9a2dfb
    , (>*<)
Packit 9a2dfb
    , InArray
Packit 9a2dfb
    , empty
Packit 9a2dfb
    , (><)
Packit 9a2dfb
    , econcat
Packit 9a2dfb
    -- ** Decimal numbers
Packit 9a2dfb
    , int8, int16, int32, int64, int
Packit 9a2dfb
    , word8, word16, word32, word64, word
Packit 9a2dfb
    , integer, float, double, scientific
Packit 9a2dfb
    -- ** Decimal numbers as Text
Packit 9a2dfb
    , int8Text, int16Text, int32Text, int64Text, intText
Packit 9a2dfb
    , word8Text, word16Text, word32Text, word64Text, wordText
Packit 9a2dfb
    , integerText, floatText, doubleText, scientificText
Packit 9a2dfb
    -- ** Time
Packit 9a2dfb
    , day
Packit 9a2dfb
    , localTime
Packit 9a2dfb
    , utcTime
Packit 9a2dfb
    , timeOfDay
Packit 9a2dfb
    , zonedTime
Packit 9a2dfb
    -- ** value
Packit 9a2dfb
    , value
Packit 9a2dfb
    -- ** JSON tokens
Packit 9a2dfb
    , comma, colon, openBracket, closeBracket, openCurly, closeCurly
Packit 9a2dfb
    ) where
Packit 9a2dfb
Packit 9a2dfb
import Prelude ()
Packit 9a2dfb
import Prelude.Compat
Packit 9a2dfb
Packit 9a2dfb
import Data.Aeson.Types.Internal (Value)
Packit 9a2dfb
import Data.ByteString.Builder (Builder, char7, toLazyByteString)
Packit 9a2dfb
import Data.Int
Packit 9a2dfb
import Data.Scientific (Scientific)
Packit 9a2dfb
import Data.Semigroup (Semigroup ((<>)))
Packit 9a2dfb
import Data.Text (Text)
Packit 9a2dfb
import Data.Time (Day, LocalTime, TimeOfDay, UTCTime, ZonedTime)
Packit 9a2dfb
import Data.Typeable (Typeable)
Packit 9a2dfb
import Data.Word
Packit 9a2dfb
import qualified Data.Aeson.Encoding.Builder as EB
Packit 9a2dfb
import qualified Data.ByteString.Builder as B
Packit 9a2dfb
import qualified Data.ByteString.Lazy as BSL
Packit 9a2dfb
import qualified Data.Text.Lazy as LT
Packit 9a2dfb
Packit 9a2dfb
-- | An encoding of a JSON value.
Packit 9a2dfb
--
Packit 9a2dfb
-- @tag@ represents which kind of JSON the Encoding is encoding to,
Packit 9a2dfb
-- we reuse 'Text' and 'Value' as tags here.
Packit 9a2dfb
newtype Encoding' tag = Encoding {
Packit 9a2dfb
      fromEncoding :: Builder
Packit 9a2dfb
      -- ^ Acquire the underlying bytestring builder.
Packit 9a2dfb
    } deriving (Typeable)
Packit 9a2dfb
Packit 9a2dfb
-- | Often used synonym for 'Encoding''.
Packit 9a2dfb
type Encoding = Encoding' Value
Packit 9a2dfb
Packit 9a2dfb
-- | Make Encoding from Builder.
Packit 9a2dfb
--
Packit 9a2dfb
-- Use with care! You have to make sure that the passed Builder
Packit 9a2dfb
-- is a valid JSON Encoding!
Packit 9a2dfb
unsafeToEncoding :: Builder -> Encoding' a
Packit 9a2dfb
unsafeToEncoding = Encoding
Packit 9a2dfb
Packit 9a2dfb
encodingToLazyByteString :: Encoding' a -> BSL.ByteString
Packit 9a2dfb
encodingToLazyByteString = toLazyByteString . fromEncoding
Packit 9a2dfb
{-# INLINE encodingToLazyByteString #-}
Packit 9a2dfb
Packit 9a2dfb
retagEncoding :: Encoding' a -> Encoding' b
Packit 9a2dfb
retagEncoding = Encoding . fromEncoding
Packit 9a2dfb
Packit 9a2dfb
-------------------------------------------------------------------------------
Packit 9a2dfb
-- Encoding instances
Packit 9a2dfb
-------------------------------------------------------------------------------
Packit 9a2dfb
Packit 9a2dfb
instance Show (Encoding' a) where
Packit 9a2dfb
    show (Encoding e) = show (toLazyByteString e)
Packit 9a2dfb
Packit 9a2dfb
instance Eq (Encoding' a) where
Packit 9a2dfb
    Encoding a == Encoding b = toLazyByteString a == toLazyByteString b
Packit 9a2dfb
Packit 9a2dfb
instance Ord (Encoding' a) where
Packit 9a2dfb
    compare (Encoding a) (Encoding b) =
Packit 9a2dfb
      compare (toLazyByteString a) (toLazyByteString b)
Packit 9a2dfb
Packit 9a2dfb
-- | A series of values that, when encoded, should be separated by
Packit 9a2dfb
-- commas. Since 0.11.0.0, the '.=' operator is overloaded to create
Packit 9a2dfb
-- either @(Text, Value)@ or 'Series'. You can use Series when
Packit 9a2dfb
-- encoding directly to a bytestring builder as in the following
Packit 9a2dfb
-- example:
Packit 9a2dfb
--
Packit 9a2dfb
-- > toEncoding (Person name age) = pairs ("name" .= name <> "age" .= age)
Packit 9a2dfb
data Series = Empty
Packit 9a2dfb
            | Value (Encoding' Series)
Packit 9a2dfb
            deriving (Typeable)
Packit 9a2dfb
Packit 9a2dfb
pair :: Text -> Encoding -> Series
Packit 9a2dfb
pair name val = pair' (text name) val
Packit 9a2dfb
{-# INLINE pair #-}
Packit 9a2dfb
Packit 9a2dfb
pairStr :: String -> Encoding -> Series
Packit 9a2dfb
pairStr name val = pair' (string name) val
Packit 9a2dfb
{-# INLINE pairStr #-}
Packit 9a2dfb
Packit 9a2dfb
pair' :: Encoding' Text -> Encoding -> Series
Packit 9a2dfb
pair' name val = Value $ retagEncoding $ retagEncoding name >< colon >< val
Packit 9a2dfb
Packit 9a2dfb
instance Semigroup Series where
Packit 9a2dfb
    Empty   <> a       = a
Packit 9a2dfb
    a       <> Empty   = a
Packit 9a2dfb
    Value a <> Value b = Value (a >< comma >< b)
Packit 9a2dfb
Packit 9a2dfb
instance Monoid Series where
Packit 9a2dfb
    mempty  = Empty
Packit 9a2dfb
    mappend = (<>)
Packit 9a2dfb
Packit 9a2dfb
nullEncoding :: Encoding' a -> Bool
Packit 9a2dfb
nullEncoding = BSL.null . toLazyByteString . fromEncoding
Packit 9a2dfb
Packit 9a2dfb
emptyArray_ :: Encoding
Packit 9a2dfb
emptyArray_ = Encoding EB.emptyArray_
Packit 9a2dfb
Packit 9a2dfb
emptyObject_ :: Encoding
Packit 9a2dfb
emptyObject_ = Encoding EB.emptyObject_
Packit 9a2dfb
Packit 9a2dfb
wrapArray :: Encoding' a -> Encoding
Packit 9a2dfb
wrapArray e = retagEncoding $ openBracket >< e >< closeBracket
Packit 9a2dfb
Packit 9a2dfb
wrapObject :: Encoding' a -> Encoding
Packit 9a2dfb
wrapObject e = retagEncoding $ openCurly >< e >< closeCurly
Packit 9a2dfb
Packit 9a2dfb
null_ :: Encoding
Packit 9a2dfb
null_ = Encoding EB.null_
Packit 9a2dfb
Packit 9a2dfb
bool :: Bool -> Encoding
Packit 9a2dfb
bool True = Encoding "true"
Packit 9a2dfb
bool False = Encoding "false"
Packit 9a2dfb
Packit 9a2dfb
-- | Encode a series of key/value pairs, separated by commas.
Packit 9a2dfb
pairs :: Series -> Encoding
Packit 9a2dfb
pairs (Value v) = openCurly >< retagEncoding v >< closeCurly
Packit 9a2dfb
pairs Empty     = emptyObject_
Packit 9a2dfb
{-# INLINE pairs #-}
Packit 9a2dfb
Packit 9a2dfb
list :: (a -> Encoding) -> [a] -> Encoding
Packit 9a2dfb
list _  []     = emptyArray_
Packit 9a2dfb
list to' (x:xs) = openBracket >< to' x >< commas xs >< closeBracket
Packit 9a2dfb
  where
Packit 9a2dfb
    commas = foldr (\v vs -> comma >< to' v >< vs) empty
Packit 9a2dfb
{-# INLINE list #-}
Packit 9a2dfb
Packit 9a2dfb
-- | Encode as JSON object
Packit 9a2dfb
dict
Packit 9a2dfb
    :: (k -> Encoding' Text)                     -- ^ key encoding
Packit 9a2dfb
    -> (v -> Encoding)                                -- ^ value encoding
Packit 9a2dfb
    -> (forall a. (k -> v -> a -> a) -> a -> m -> a)  -- ^ @foldrWithKey@ - indexed fold
Packit 9a2dfb
    -> m                                              -- ^ container
Packit 9a2dfb
    -> Encoding
Packit 9a2dfb
dict encodeKey encodeVal foldrWithKey = pairs . foldrWithKey go mempty
Packit 9a2dfb
  where
Packit 9a2dfb
    go k v c = Value (encodeKV k v) <> c
Packit 9a2dfb
    encodeKV k v = retagEncoding (encodeKey k) >< colon >< retagEncoding (encodeVal v)
Packit 9a2dfb
{-# INLINE dict #-}
Packit 9a2dfb
Packit 9a2dfb
-- | Type tag for tuples contents, see 'tuple'.
Packit 9a2dfb
data InArray
Packit 9a2dfb
Packit 9a2dfb
infixr 6 >*<
Packit 9a2dfb
-- | See 'tuple'.
Packit 9a2dfb
(>*<) :: Encoding' a -> Encoding' b -> Encoding' InArray
Packit 9a2dfb
a >*< b = retagEncoding a >< comma >< retagEncoding b
Packit 9a2dfb
{-# INLINE (>*<) #-}
Packit 9a2dfb
Packit 9a2dfb
empty :: Encoding' a
Packit 9a2dfb
empty = Encoding mempty
Packit 9a2dfb
Packit 9a2dfb
econcat :: [Encoding' a] -> Encoding' a
Packit 9a2dfb
econcat = foldr (><) empty
Packit 9a2dfb
Packit 9a2dfb
infixr 6 ><
Packit 9a2dfb
(><) :: Encoding' a -> Encoding' a -> Encoding' a
Packit 9a2dfb
Encoding a >< Encoding b = Encoding (a <> b)
Packit 9a2dfb
{-# INLINE (><) #-}
Packit 9a2dfb
Packit 9a2dfb
-- | Encode as a tuple.
Packit 9a2dfb
--
Packit 9a2dfb
-- @
Packit 9a2dfb
-- toEncoding (X a b c) = tuple $
Packit 9a2dfb
--     toEncoding a >*<
Packit 9a2dfb
--     toEncoding b >*<
Packit 9a2dfb
--     toEncoding c
Packit 9a2dfb
tuple :: Encoding' InArray -> Encoding
Packit 9a2dfb
tuple b = retagEncoding $ openBracket >< b >< closeBracket
Packit 9a2dfb
{-# INLINE tuple #-}
Packit 9a2dfb
Packit 9a2dfb
text :: Text -> Encoding' a
Packit 9a2dfb
text = Encoding . EB.text
Packit 9a2dfb
Packit 9a2dfb
lazyText :: LT.Text -> Encoding' a
Packit 9a2dfb
lazyText t = Encoding $
Packit 9a2dfb
    B.char7 '"' <>
Packit 9a2dfb
    LT.foldrChunks (\x xs -> EB.unquoted x <> xs) (B.char7 '"') t
Packit 9a2dfb
Packit 9a2dfb
string :: String -> Encoding' a
Packit 9a2dfb
string = Encoding . EB.string
Packit 9a2dfb
Packit 9a2dfb
-------------------------------------------------------------------------------
Packit 9a2dfb
-- chars
Packit 9a2dfb
-------------------------------------------------------------------------------
Packit 9a2dfb
Packit 9a2dfb
comma, colon, openBracket, closeBracket, openCurly, closeCurly :: Encoding' a
Packit 9a2dfb
comma        = Encoding $ char7 ','
Packit 9a2dfb
colon        = Encoding $ char7 ':'
Packit 9a2dfb
openBracket  = Encoding $ char7 '['
Packit 9a2dfb
closeBracket = Encoding $ char7 ']'
Packit 9a2dfb
openCurly    = Encoding $ char7 '{'
Packit 9a2dfb
closeCurly   = Encoding $ char7 '}'
Packit 9a2dfb
Packit 9a2dfb
-------------------------------------------------------------------------------
Packit 9a2dfb
-- Decimal numbers
Packit 9a2dfb
-------------------------------------------------------------------------------
Packit 9a2dfb
Packit 9a2dfb
int8 :: Int8 -> Encoding
Packit 9a2dfb
int8 = Encoding . B.int8Dec
Packit 9a2dfb
Packit 9a2dfb
int16 :: Int16 -> Encoding
Packit 9a2dfb
int16 = Encoding . B.int16Dec
Packit 9a2dfb
Packit 9a2dfb
int32 :: Int32 -> Encoding
Packit 9a2dfb
int32 = Encoding . B.int32Dec
Packit 9a2dfb
Packit 9a2dfb
int64 :: Int64 -> Encoding
Packit 9a2dfb
int64 = Encoding . B.int64Dec
Packit 9a2dfb
Packit 9a2dfb
int :: Int -> Encoding
Packit 9a2dfb
int = Encoding . B.intDec
Packit 9a2dfb
Packit 9a2dfb
word8 :: Word8 -> Encoding
Packit 9a2dfb
word8 = Encoding . B.word8Dec
Packit 9a2dfb
Packit 9a2dfb
word16 :: Word16 -> Encoding
Packit 9a2dfb
word16 = Encoding . B.word16Dec
Packit 9a2dfb
Packit 9a2dfb
word32 :: Word32 -> Encoding
Packit 9a2dfb
word32 = Encoding . B.word32Dec
Packit 9a2dfb
Packit 9a2dfb
word64 :: Word64 -> Encoding
Packit 9a2dfb
word64 = Encoding . B.word64Dec
Packit 9a2dfb
Packit 9a2dfb
word :: Word -> Encoding
Packit 9a2dfb
word = Encoding . B.wordDec
Packit 9a2dfb
Packit 9a2dfb
integer :: Integer -> Encoding
Packit 9a2dfb
integer = Encoding . B.integerDec
Packit 9a2dfb
Packit 9a2dfb
float :: Float -> Encoding
Packit 9a2dfb
float = realFloatToEncoding $ Encoding . B.floatDec
Packit 9a2dfb
Packit 9a2dfb
double :: Double -> Encoding
Packit 9a2dfb
double = realFloatToEncoding $ Encoding . B.doubleDec
Packit 9a2dfb
Packit 9a2dfb
scientific :: Scientific -> Encoding
Packit 9a2dfb
scientific = Encoding . EB.scientific
Packit 9a2dfb
Packit 9a2dfb
realFloatToEncoding :: RealFloat a => (a -> Encoding) -> a -> Encoding
Packit 9a2dfb
realFloatToEncoding e d
Packit 9a2dfb
    | isNaN d || isInfinite d = null_
Packit 9a2dfb
    | otherwise               = e d
Packit 9a2dfb
{-# INLINE realFloatToEncoding #-}
Packit 9a2dfb
Packit 9a2dfb
-------------------------------------------------------------------------------
Packit 9a2dfb
-- Decimal numbers as Text
Packit 9a2dfb
-------------------------------------------------------------------------------
Packit 9a2dfb
Packit 9a2dfb
int8Text :: Int8 -> Encoding' a
Packit 9a2dfb
int8Text = Encoding . EB.quote . B.int8Dec
Packit 9a2dfb
Packit 9a2dfb
int16Text :: Int16 -> Encoding' a
Packit 9a2dfb
int16Text = Encoding . EB.quote . B.int16Dec
Packit 9a2dfb
Packit 9a2dfb
int32Text :: Int32 -> Encoding' a
Packit 9a2dfb
int32Text = Encoding . EB.quote . B.int32Dec
Packit 9a2dfb
Packit 9a2dfb
int64Text :: Int64 -> Encoding' a
Packit 9a2dfb
int64Text = Encoding . EB.quote . B.int64Dec
Packit 9a2dfb
Packit 9a2dfb
intText :: Int -> Encoding' a
Packit 9a2dfb
intText = Encoding . EB.quote . B.intDec
Packit 9a2dfb
Packit 9a2dfb
word8Text :: Word8 -> Encoding' a
Packit 9a2dfb
word8Text = Encoding . EB.quote . B.word8Dec
Packit 9a2dfb
Packit 9a2dfb
word16Text :: Word16 -> Encoding' a
Packit 9a2dfb
word16Text = Encoding . EB.quote . B.word16Dec
Packit 9a2dfb
Packit 9a2dfb
word32Text :: Word32 -> Encoding' a
Packit 9a2dfb
word32Text = Encoding . EB.quote . B.word32Dec
Packit 9a2dfb
Packit 9a2dfb
word64Text :: Word64 -> Encoding' a
Packit 9a2dfb
word64Text = Encoding . EB.quote . B.word64Dec
Packit 9a2dfb
Packit 9a2dfb
wordText :: Word -> Encoding' a
Packit 9a2dfb
wordText = Encoding . EB.quote . B.wordDec
Packit 9a2dfb
Packit 9a2dfb
integerText :: Integer -> Encoding' a
Packit 9a2dfb
integerText = Encoding . EB.quote . B.integerDec
Packit 9a2dfb
Packit 9a2dfb
floatText :: Float -> Encoding' a
Packit 9a2dfb
floatText = Encoding . EB.quote . B.floatDec
Packit 9a2dfb
Packit 9a2dfb
doubleText :: Double -> Encoding' a
Packit 9a2dfb
doubleText = Encoding . EB.quote . B.doubleDec
Packit 9a2dfb
Packit 9a2dfb
scientificText :: Scientific -> Encoding' a
Packit 9a2dfb
scientificText = Encoding . EB.quote . EB.scientific
Packit 9a2dfb
Packit 9a2dfb
-------------------------------------------------------------------------------
Packit 9a2dfb
-- time
Packit 9a2dfb
-------------------------------------------------------------------------------
Packit 9a2dfb
Packit 9a2dfb
day :: Day -> Encoding' a
Packit 9a2dfb
day = Encoding . EB.quote . EB.day
Packit 9a2dfb
Packit 9a2dfb
localTime :: LocalTime -> Encoding' a
Packit 9a2dfb
localTime = Encoding . EB.quote . EB.localTime
Packit 9a2dfb
Packit 9a2dfb
utcTime :: UTCTime -> Encoding' a
Packit 9a2dfb
utcTime = Encoding . EB.quote . EB.utcTime
Packit 9a2dfb
Packit 9a2dfb
timeOfDay :: TimeOfDay -> Encoding' a
Packit 9a2dfb
timeOfDay = Encoding . EB.quote . EB.timeOfDay
Packit 9a2dfb
Packit 9a2dfb
zonedTime :: ZonedTime -> Encoding' a
Packit 9a2dfb
zonedTime = Encoding . EB.quote . EB.zonedTime
Packit 9a2dfb
Packit 9a2dfb
-------------------------------------------------------------------------------
Packit 9a2dfb
-- Value
Packit 9a2dfb
-------------------------------------------------------------------------------
Packit 9a2dfb
Packit 9a2dfb
value :: Value -> Encoding
Packit 9a2dfb
value = Encoding . EB.encodeToBuilder