{-# LANGUAGE BangPatterns #-}
module Codec.CBOR.JSON
( encodeValue
, decodeValue
) where
import Data.Monoid
import Control.Applicative
import Prelude
import Codec.CBOR.Encoding
import Codec.CBOR.Decoding
import Data.Aeson ( Value(..) )
import qualified Data.Aeson as Aeson
import qualified Data.HashMap.Lazy as HM
import Data.Scientific as Scientific
import qualified Data.Text as T
import qualified Data.Vector as V
encodeValue :: Value -> Encoding
encodeValue (Object vs) = encodeObject vs
encodeValue (Array vs) = encodeArray vs
encodeValue (String s) = encodeString s
encodeValue (Number n) = case Scientific.floatingOrInteger n of
Left d -> encodeDouble d
Right i -> encodeInteger i
encodeValue (Bool b) = encodeBool b
encodeValue Null = encodeNull
encodeObject :: Aeson.Object -> Encoding
encodeObject vs =
encodeMapLen (fromIntegral (HM.size vs))
<> HM.foldrWithKey (\k v r -> encodeString k <> encodeValue v <> r) mempty vs
encodeArray :: Aeson.Array -> Encoding
encodeArray vs =
encodeListLen (fromIntegral (V.length vs))
<> V.foldr (\v r -> encodeValue v <> r) mempty vs
decodeValue :: Bool -> Decoder s Value
decodeValue lenient = do
tkty <- peekTokenType
case tkty of
TypeUInt -> decodeNumberIntegral
TypeUInt64 -> decodeNumberIntegral
TypeNInt -> decodeNumberIntegral
TypeNInt64 -> decodeNumberIntegral
TypeInteger -> decodeNumberIntegral
TypeFloat64 -> decodeNumberFloating
TypeBool -> Bool <$> decodeBool
TypeNull -> Null <$ decodeNull
TypeString -> String <$> decodeString
TypeListLen -> decodeListLen >>= flip (decodeListN lenient) []
TypeListLenIndef -> decodeListLenIndef >> (decodeListIndef lenient) []
TypeMapLen -> decodeMapLen >>= flip (decodeMapN lenient) HM.empty
_ -> fail $ "unexpected CBOR token type for a JSON value: "
++ show tkty
decodeNumberIntegral :: Decoder s Value
decodeNumberIntegral = Number . fromInteger <$> decodeInteger
decodeNumberFloating :: Decoder s Value
decodeNumberFloating = Number . Scientific.fromFloatDigits <$> decodeDouble
decodeListN :: Bool -> Int -> [Value] -> Decoder s Value
decodeListN !lenient !n acc =
case n of
0 -> return $! Array (V.fromList (reverse acc))
_ -> do !t <- decodeValue lenient
decodeListN lenient (n-1) (t : acc)
decodeListIndef :: Bool -> [Value] -> Decoder s Value
decodeListIndef !lenient acc = do
stop <- decodeBreakOr
if stop then return $! Array (V.fromList (reverse acc))
else do !tm <- decodeValue lenient
decodeListIndef lenient (tm : acc)
decodeMapN :: Bool -> Int -> Aeson.Object -> Decoder s Value
decodeMapN !lenient !n acc =
case n of
0 -> return $! Object acc
_ -> do
!tk <- decodeValue lenient >>= \v -> case v of
String s -> return s
Number d | lenient -> return $ T.pack (show d)
Bool b | lenient -> return $ T.pack (show b)
_ -> fail "Could not decode map key type"
!tv <- decodeValue lenient
decodeMapN lenient (n-1) (HM.insert tk tv acc)