module Data.Aeson.Types.Internal
(
Value(..)
, Encoding(..)
, Series(..)
, Array
, emptyArray, isEmptyArray
, Pair
, Object
, emptyObject
, Parser
, Result(..)
, IResult(..)
, JSONPathElement(..)
, JSONPath
, iparse
, parse
, parseEither
, parseMaybe
, modifyFailure
, formatError
, (<?>)
, object
, Options(..)
, SumEncoding(..)
, defaultOptions
, defaultTaggedObject
, camelTo
, camelTo2
, DotNetTime(..)
) where
import Control.Applicative
import Control.DeepSeq (NFData(..))
import Control.Monad (MonadPlus(..), ap)
import Data.ByteString.Builder (Builder, char7, toLazyByteString)
import Data.Char (isLower, isUpper, toLower)
import Data.Data (Data)
import Data.Foldable (Foldable(..))
import Data.HashMap.Strict (HashMap)
import Data.Hashable (Hashable(..))
import Data.Monoid (Monoid(..), (<>))
import Data.Scientific (Scientific)
import Data.String (IsString(..))
import Data.Text (Text, pack, unpack)
import Data.Time (UTCTime)
import Data.Time.Format (FormatTime)
import Data.Traversable (Traversable(..))
import Data.Typeable (Typeable)
import Data.Vector (Vector)
import qualified Data.HashMap.Strict as H
import qualified Data.Vector as V
data JSONPathElement = Key Text
| Index !Int
deriving (Eq, Show, Typeable)
type JSONPath = [JSONPathElement]
data IResult a = IError JSONPath String
| ISuccess a
deriving (Eq, Show, Typeable)
data Result a = Error String
| Success a
deriving (Eq, Show, Typeable)
instance NFData JSONPathElement where
rnf (Key t) = rnf t
rnf (Index i) = rnf i
instance (NFData a) => NFData (IResult a) where
rnf (ISuccess a) = rnf a
rnf (IError path err) = rnf path `seq` rnf err
instance (NFData a) => NFData (Result a) where
rnf (Success a) = rnf a
rnf (Error err) = rnf err
instance Functor IResult where
fmap f (ISuccess a) = ISuccess (f a)
fmap _ (IError path err) = IError path err
instance Functor Result where
fmap f (Success a) = Success (f a)
fmap _ (Error err) = Error err
instance Monad IResult where
return = ISuccess
ISuccess a >>= k = k a
IError path err >>= _ = IError path err
fail err = IError [] err
instance Monad Result where
return = Success
Success a >>= k = k a
Error err >>= _ = Error err
fail err = Error err
instance Applicative IResult where
pure = return
(<*>) = ap
instance Applicative Result where
pure = return
(<*>) = ap
instance MonadPlus IResult where
mzero = fail "mzero"
mplus a@(ISuccess _) _ = a
mplus _ b = b
instance MonadPlus Result where
mzero = fail "mzero"
mplus a@(Success _) _ = a
mplus _ b = b
instance Alternative IResult where
empty = mzero
(<|>) = mplus
instance Alternative Result where
empty = mzero
(<|>) = mplus
instance Monoid (IResult a) where
mempty = fail "mempty"
mappend = mplus
instance Monoid (Result a) where
mempty = fail "mempty"
mappend = mplus
instance Foldable IResult where
foldMap _ (IError _ _) = mempty
foldMap f (ISuccess y) = f y
foldr _ z (IError _ _) = z
foldr f z (ISuccess y) = f y z
instance Foldable Result where
foldMap _ (Error _) = mempty
foldMap f (Success y) = f y
foldr _ z (Error _) = z
foldr f z (Success y) = f y z
instance Traversable IResult where
traverse _ (IError path err) = pure (IError path err)
traverse f (ISuccess a) = ISuccess <$> f a
instance Traversable Result where
traverse _ (Error err) = pure (Error err)
traverse f (Success a) = Success <$> f a
type Failure f r = JSONPath -> String -> f r
type Success a f r = a -> f r
newtype Parser a = Parser {
runParser :: forall f r.
JSONPath
-> Failure f r
-> Success a f r
-> f r
}
instance Monad Parser where
m >>= g = Parser $ \path kf ks -> let ks' a = runParser (g a) path kf ks
in runParser m path kf ks'
return a = Parser $ \_path _kf ks -> ks a
fail msg = Parser $ \path kf _ks -> kf (reverse path) msg
instance Functor Parser where
fmap f m = Parser $ \path kf ks -> let ks' a = ks (f a)
in runParser m path kf ks'
instance Applicative Parser where
pure = return
(<*>) = apP
instance Alternative Parser where
empty = fail "empty"
(<|>) = mplus
instance MonadPlus Parser where
mzero = fail "mzero"
mplus a b = Parser $ \path kf ks -> let kf' _ _ = runParser b path kf ks
in runParser a path kf' ks
instance Monoid (Parser a) where
mempty = fail "mempty"
mappend = mplus
apP :: Parser (a -> b) -> Parser a -> Parser b
apP d e = do
b <- d
a <- e
return (b a)
type Object = HashMap Text Value
type Array = Vector Value
data Value = Object !Object
| Array !Array
| String !Text
| Number !Scientific
| Bool !Bool
| Null
deriving (Eq, Read, Show, Typeable, Data)
newtype Encoding = Encoding {
fromEncoding :: Builder
} deriving (Monoid)
instance Show Encoding where
show (Encoding e) = show (toLazyByteString e)
instance Eq Encoding where
Encoding a == Encoding b = toLazyByteString a == toLazyByteString b
instance Ord Encoding where
compare (Encoding a) (Encoding b) =
compare (toLazyByteString a) (toLazyByteString b)
data Series = Empty
| Value Encoding
deriving (Typeable)
instance Monoid Series where
mempty = Empty
mappend Empty a = a
mappend (Value a) b =
Value $
a <> case b of
Empty -> mempty
Value c -> Encoding (char7 ',') <> c
newtype DotNetTime = DotNetTime {
fromDotNetTime :: UTCTime
} deriving (Eq, Ord, Read, Show, Typeable, FormatTime)
instance NFData Value where
rnf (Object o) = rnf o
rnf (Array a) = V.foldl' (\x y -> rnf y `seq` x) () a
rnf (String s) = rnf s
rnf (Number n) = rnf n
rnf (Bool b) = rnf b
rnf Null = ()
instance IsString Value where
fromString = String . pack
hashValue :: Int -> Value -> Int
hashValue s (Object o) = H.foldl' hashWithSalt
(s `hashWithSalt` (0::Int)) o
hashValue s (Array a) = V.foldl' hashWithSalt
(s `hashWithSalt` (1::Int)) a
hashValue s (String str) = s `hashWithSalt` (2::Int) `hashWithSalt` str
hashValue s (Number n) = s `hashWithSalt` (3::Int) `hashWithSalt` n
hashValue s (Bool b) = s `hashWithSalt` (4::Int) `hashWithSalt` b
hashValue s Null = s `hashWithSalt` (5::Int)
instance Hashable Value where
hashWithSalt = hashValue
emptyArray :: Value
emptyArray = Array V.empty
isEmptyArray :: Value -> Bool
isEmptyArray (Array arr) = V.null arr
isEmptyArray _ = False
emptyObject :: Value
emptyObject = Object H.empty
parse :: (a -> Parser b) -> a -> Result b
parse m v = runParser (m v) [] (const Error) Success
iparse :: (a -> Parser b) -> a -> IResult b
iparse m v = runParser (m v) [] IError ISuccess
parseMaybe :: (a -> Parser b) -> a -> Maybe b
parseMaybe m v = runParser (m v) [] (\_ _ -> Nothing) Just
parseEither :: (a -> Parser b) -> a -> Either String b
parseEither m v = runParser (m v) [] onError Right
where onError path msg = Left (formatError path msg)
formatError :: JSONPath -> String -> String
formatError path msg = "Error in " ++ (format "$" path) ++ ": " ++ msg
where
format pfx [] = pfx
format pfx (Index idx:parts) = format (pfx ++ "[" ++ show idx ++ "]") parts
format pfx (Key key:parts) = format (pfx ++ "." ++ unpack key) parts
type Pair = (Text, Value)
object :: [Pair] -> Value
object = Object . H.fromList
(<?>) :: Parser a -> JSONPathElement -> Parser a
p <?> pathElem = Parser $ \path kf ks -> runParser p (pathElem:path) kf ks
modifyFailure :: (String -> String) -> Parser a -> Parser a
modifyFailure f (Parser p) = Parser $ \path kf ks -> p path (\p' m -> kf p' (f m)) ks
data Options = Options
{ fieldLabelModifier :: String -> String
, constructorTagModifier :: String -> String
, allNullaryToStringTag :: Bool
, omitNothingFields :: Bool
, sumEncoding :: SumEncoding
, unwrapUnaryRecords :: Bool
}
instance Show Options where
show Options{..} = "Options {" ++
"fieldLabelModifier =~ " ++
show (fieldLabelModifier "exampleField") ++ ", " ++
"constructorTagModifier =~ " ++
show (constructorTagModifier "ExampleConstructor") ++ ", " ++
"allNullaryToStringTag = " ++ show allNullaryToStringTag ++ ", " ++
"omitNothingFields = " ++ show omitNothingFields ++ ", " ++
"sumEncoding = " ++ show sumEncoding ++ ", " ++
"unwrapUnaryRecords = " ++ show unwrapUnaryRecords ++
"}"
data SumEncoding =
TaggedObject { tagFieldName :: String
, contentsFieldName :: String
}
| ObjectWithSingleField
| TwoElemArray
deriving (Eq, Show)
defaultOptions :: Options
defaultOptions = Options
{ fieldLabelModifier = id
, constructorTagModifier = id
, allNullaryToStringTag = True
, omitNothingFields = False
, sumEncoding = defaultTaggedObject
, unwrapUnaryRecords = False
}
defaultTaggedObject :: SumEncoding
defaultTaggedObject = TaggedObject
{ tagFieldName = "tag"
, contentsFieldName = "contents"
}
camelTo :: Char -> String -> String
camelTo c = lastWasCap True
where
lastWasCap :: Bool
-> String
-> String
lastWasCap _ [] = []
lastWasCap prev (x : xs) = if isUpper x
then if prev
then toLower x : lastWasCap True xs
else c : toLower x : lastWasCap True xs
else x : lastWasCap False xs
camelTo2 :: Char -> String -> String
camelTo2 c = map toLower . go2 . go1
where go1 "" = ""
go1 (x:u:l:xs) | isUpper u && isLower l = x : c : u : l : go1 xs
go1 (x:xs) = x : go1 xs
go2 "" = ""
go2 (l:u:xs) | isLower l && isUpper u = l : c : u : go2 xs
go2 (x:xs) = x : go2 xs