{-# LANGUAGE PolyKinds #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module Data.Exists.Aeson ( FromJSONForall(..) , FromJSONForeach(..) , FromJSONExists(..) , ToJSONForall(..) , ToJSONForeach(..) , ToJSONKeyFunctionForall(..) , FromJSONKeyFunctionForeach(..) , ToJSONKeyForall(..) , ToJSONKeyForeach(..) , FromJSONKeyExists(..) , FromJSONKeyForeach(..) , ToJSONSing(..) , FromJSONSing(..) , parseJSONMapForeachKey , toJSONMapForeachKey ) where import Control.Applicative (Const(..)) import Data.Aeson (()) import Data.Aeson (ToJSON(..),FromJSON(..)) import Data.Aeson (ToJSONKey(..),FromJSONKey(..)) import Data.Aeson (ToJSONKeyFunction(..),FromJSONKeyFunction(..)) import Data.Aeson.Types (JSONPathElement(Key,Index)) import Data.Coerce (coerce) import Data.Exists (Exists(..),Some(..),Sing,ApplyForeach(..),OrdForeach) import Data.Exists (Reify(..),Unreify(..)) import Data.Functor.Compose (Compose(..)) import Data.Functor.Product (Product(..)) import Data.Kind (Type) import Data.Map.Strict (Map) import qualified Data.Aeson.Encoding as Aeson import qualified Data.Aeson.Encoding.Internal as AEI import qualified Data.Aeson.Key as Key import qualified Data.Aeson.KeyMap as KM import qualified Data.Aeson.Types as Aeson import qualified Data.HashMap.Strict as HM import qualified Data.Map.Strict as M import qualified Data.Traversable as TRV import qualified Data.Vector as V data ToJSONKeyFunctionForall f = ToJSONKeyTextForall !(forall a. f a -> Aeson.Key) !(forall a. f a -> Aeson.Encoding' Aeson.Key) | ToJSONKeyValueForall !(forall a. f a -> Aeson.Value) !(forall a. f a -> Aeson.Encoding) data FromJSONKeyFunctionForeach f = FromJSONKeyTextParserForeach !(forall a. Sing a -> Aeson.Key -> Aeson.Parser (f a)) | FromJSONKeyValueForeach !(forall a. Sing a -> Aeson.Value -> Aeson.Parser (f a)) instance (ToJSONForeach f, Reify a) => ToJSON (ApplyForeach f a) where toJSON = toJSONForeach reify instance (FromJSONForeach f, Reify a) => FromJSON (ApplyForeach f a) where parseJSON = parseJSONForeach reify instance ToJSONForeach f => ToJSONForeach (ApplyForeach f) where toJSONForeach s (ApplyForeach x) = toJSONForeach s x instance FromJSONForeach f => FromJSONForeach (ApplyForeach f) where parseJSONForeach s = fmap ApplyForeach . parseJSONForeach s instance ToJSONKeyForeach f => ToJSONKeyForeach (ApplyForeach f) where toJSONKeyForeach = case toJSONKeyForeach of ToJSONKeyTextForall f g -> ToJSONKeyTextForall (\(Pair s (ApplyForeach x)) -> f (Pair s x)) (\(Pair s (ApplyForeach x)) -> g (Pair s x)) ToJSONKeyValueForall f g -> ToJSONKeyValueForall (\(Pair s (ApplyForeach x)) -> f (Pair s x)) (\(Pair s (ApplyForeach x)) -> g (Pair s x)) instance FromJSONKeyForeach f => FromJSONKeyForeach (ApplyForeach f) where fromJSONKeyForeach = case fromJSONKeyForeach of FromJSONKeyTextParserForeach f -> FromJSONKeyTextParserForeach (\s t -> fmap ApplyForeach (f s t)) FromJSONKeyValueForeach f -> FromJSONKeyValueForeach (\s t -> fmap ApplyForeach (f s t)) instance (ToJSONKeyForeach f, Reify a) => ToJSONKey (ApplyForeach f a) where toJSONKey = case toJSONKeyForeach of ToJSONKeyTextForall toText toEnc -> ToJSONKeyText (\(ApplyForeach x) -> toText (Pair reify x)) (\(ApplyForeach x) -> toEnc (Pair reify x)) ToJSONKeyValueForall toValue toEnc -> ToJSONKeyValue (\(ApplyForeach x) -> toValue (Pair reify x)) (\(ApplyForeach x) -> toEnc (Pair reify x)) toJSONKeyList = case toJSONKeyForeach of ToJSONKeyTextForall toText toEnc -> ToJSONKeyValue (\xs -> toJSON $ map (\(ApplyForeach x) -> toText (Pair reify x)) xs) (\xs -> Aeson.list (textEncodingToValueEncoding . toEnc . Pair reify) (map getApplyForeach xs)) ToJSONKeyValueForall toValue toEnc -> ToJSONKeyValue (\xs -> toJSON $ map (\(ApplyForeach x) -> toValue (Pair reify x)) xs) (\xs -> Aeson.list (toEnc . Pair reify) (map getApplyForeach xs)) -- this is always safe textEncodingToValueEncoding :: Aeson.Encoding' Aeson.Key -> Aeson.Encoding' Aeson.Value textEncodingToValueEncoding = AEI.retagEncoding instance (FromJSONKeyForeach f, Reify a) => FromJSONKey (ApplyForeach f a) where fromJSONKey = case fromJSONKeyForeach of FromJSONKeyTextParserForeach f -> FromJSONKeyTextParser (fmap ApplyForeach . f reify . Key.fromText) FromJSONKeyValueForeach f -> FromJSONKeyValue (fmap ApplyForeach . f reify) fromJSONKeyList = case fromJSONKeyForeach of FromJSONKeyTextParserForeach f -> FromJSONKeyValue $ Aeson.withArray "ApplyForeach" $ \xs -> do fmap V.toList (mapM (fmap ApplyForeach . Aeson.withText "ApplyForeach" (f reify . Key.fromText)) xs) FromJSONKeyValueForeach f -> FromJSONKeyValue $ Aeson.withArray "ApplyForeach" $ \xs -> do fmap V.toList (mapM (fmap ApplyForeach . f reify) xs) class ToJSONKeyForall f where toJSONKeyForall :: ToJSONKeyFunctionForall f class ToJSONKeyForeach f where toJSONKeyForeach :: ToJSONKeyFunctionForall (Product Sing f) class FromJSONKeyExists f where fromJSONKeyExists :: FromJSONKeyFunction (Exists f) class FromJSONKeyForeach f where fromJSONKeyForeach :: FromJSONKeyFunctionForeach f class ToJSONForall f where toJSONForall :: f a -> Aeson.Value class ToJSONForeach f where toJSONForeach :: Sing a -> f a -> Aeson.Value class FromJSONForall f where parseJSONForall :: Sing a -> Aeson.Value -> Aeson.Parser (f a) class FromJSONForeach f where parseJSONForeach :: Sing a -> Aeson.Value -> Aeson.Parser (f a) class FromJSONExists f where parseJSONExists :: Aeson.Value -> Aeson.Parser (Exists f) instance FromJSON a => FromJSONForeach (Const a) where parseJSONForeach _ = fmap Const . parseJSON instance ToJSON a => ToJSONForeach (Const a) where toJSONForeach _ = coerce (toJSON @a) -- I need to get rid of the ToJSONForall and FromJSONForeach constraints -- on these two instances. instance (ToJSONKeyForall f, ToJSONForall f) => ToJSONKey (Exists f) where toJSONKey = case toJSONKeyForall of ToJSONKeyTextForall t e -> ToJSONKeyText (\(Exists a) -> t a) (\(Exists a) -> e a) ToJSONKeyValueForall v e -> ToJSONKeyValue (\x -> case x of Exists a -> v a) (\(Exists a) -> e a) instance (FromJSONKeyExists f, FromJSONExists f) => FromJSONKey (Exists f) where fromJSONKey = fromJSONKeyExists instance ToJSONForall f => ToJSON (Exists f) where toJSON (Exists a) = toJSONForall a instance FromJSONExists f => FromJSON (Exists f) where parseJSON v = parseJSONExists v instance (Aeson.ToJSON1 f, ToJSONForall g) => ToJSONForall (Compose f g) where toJSONForall (Compose x) = Aeson.liftToJSON (\_ -> False) toJSONForall (Aeson.toJSON . map toJSONForall) x instance (Aeson.ToJSON1 f, ToJSONForeach g) => ToJSONForeach (Compose f g) where toJSONForeach s (Compose x) = Aeson.liftToJSON (\_ -> False) (toJSONForeach s) (Aeson.toJSON . map (toJSONForeach s)) x instance (Aeson.FromJSON1 f, FromJSONForeach g) => FromJSONForeach (Compose f g) where parseJSONForeach s = fmap Compose . Aeson.liftParseJSON Nothing (parseJSONForeach s) (Aeson.withArray "Compose" (fmap V.toList . V.mapM (parseJSONForeach s))) class ToJSONSing k where toJSONSing :: forall (a :: k). Sing a -> Aeson.Value instance (ToJSONForeach f, ToJSONSing k) => ToJSON (Some (f :: k -> Type)) where toJSON (Some s v) = toJSON [toJSONSing s, toJSONForeach s v] class FromJSONSing k where parseJSONSing :: Aeson.Value -> Aeson.Parser (Exists (Sing :: k -> Type)) instance (Aeson.FromJSON1 f, FromJSONForall g) => FromJSONForall (Compose f g) where parseJSONForall s = fmap Compose . Aeson.liftParseJSON Nothing (parseJSONForall s) (Aeson.withArray "Compose" (fmap V.toList . V.mapM (parseJSONForall s))) instance (FromJSONForeach f, FromJSONSing k) => FromJSON (Some (f :: k -> Type)) where parseJSON = Aeson.withArray "Some" $ \v -> if V.length v == 2 then do let x = V.unsafeIndex v 0 y = V.unsafeIndex v 1 Exists s <- parseJSONSing x :: Aeson.Parser (Exists (Sing :: k -> Type)) val <- parseJSONForeach s y return (Some s val) else fail "array of length 2 expected" -- This name is not great. I need to figure out a better naming -- scheme that allows this area to grow. toJSONMapForeachKey :: (ToJSONKeyForeach f, ToJSONForeach v) => Sing a -> Map (f a) (v a) -> Aeson.Value toJSONMapForeachKey s m = case toJSONKeyForeach of ToJSONKeyTextForall keyToText _ -> toJSON $ M.foldlWithKey' ( \hm key val -> HM.insert (keyToText (Pair s key)) (toJSONForeach s val) hm ) HM.empty m ToJSONKeyValueForall keyToValue _ -> toJSON $ M.foldrWithKey' ( \key val xs -> (keyToValue (Pair s key), toJSONForeach s val) : xs ) [] m -- | Parse a 'Map' whose key type is higher-kinded. This only creates a valid 'Map' -- if the 'OrdForeach' instance agrees with the 'Ord' instance. parseJSONMapForeachKey :: forall k (f :: k -> Type) (a :: k) v. (FromJSONKeyForeach f, OrdForeach f, Unreify k) => (Aeson.Value -> Aeson.Parser v) -> Sing a -> Aeson.Value -> Aeson.Parser (Map (f a) v) parseJSONMapForeachKey valueParser s obj = unreify s $ case fromJSONKeyForeach of FromJSONKeyTextParserForeach f -> Aeson.withObject "Map k v" ( fmap (M.mapKeysMonotonic getApplyForeach) . KM.foldrWithKey (\k v m -> M.insert <$> (coerce (f s k :: Aeson.Parser (f a)) :: Aeson.Parser (ApplyForeach f a)) Key k <*> valueParser v Key k <*> m ) (pure M.empty) ) obj FromJSONKeyValueForeach f -> Aeson.withArray "Map k v" ( fmap (M.mapKeysMonotonic getApplyForeach . M.fromList) . (coerce :: Aeson.Parser [(f a, v)] -> Aeson.Parser [(ApplyForeach f a, v)]) . TRV.sequence . zipWith (parseIndexedJSONPair (f s) valueParser) [0..] . V.toList ) obj -- copied from aeson parseIndexedJSONPair :: (Aeson.Value -> Aeson.Parser a) -> (Aeson.Value -> Aeson.Parser b) -> Int -> Aeson.Value -> Aeson.Parser (a, b) parseIndexedJSONPair keyParser valParser idx value = p value Index idx where p = Aeson.withArray "(k,v)" $ \ab -> let n = V.length ab in if n == 2 then (,) <$> parseJSONElemAtIndex keyParser 0 ab <*> parseJSONElemAtIndex valParser 1 ab else fail $ "cannot unpack array of length " ++ show n ++ " into a pair" {-# INLINE parseIndexedJSONPair #-} -- copied from aeson parseJSONElemAtIndex :: (Aeson.Value -> Aeson.Parser a) -> Int -> V.Vector Aeson.Value -> Aeson.Parser a parseJSONElemAtIndex p idx ary = p (V.unsafeIndex ary idx) Index idx