{-# LANGUAGE ScopedTypeVariables, DeriveDataTypeable, DefaultSignatures, TypeOperators, TupleSections, FlexibleContexts, FlexibleInstances, LambdaCase #-} module GHCJS.Marshal.Internal ( FromJSVal(..) , ToJSVal(..) , PToJSVal(..) , PFromJSVal(..) , Purity(..) , toJSVal_generic , fromJSVal_generic , toJSVal_pure , fromJSVal_pure , fromJSValUnchecked_pure ) where import Control.Monad import Data.Data import Data.Maybe import Data.Coerce (coerce) import qualified Data.Text as T (pack) import GHC.Generics import Language.Javascript.JSaddle.Types (JSM, JSVal, SomeJSArray(..), MutableJSArray, JSString) import Language.Javascript.JSaddle.Foreign (jsNull, jsTrue, isUndefinedIO) import Language.Javascript.JSaddle.String (textToStr) import qualified JavaScript.Object.Internal as OI (Object(..), create, setProp, getProp) import qualified JavaScript.Array.Internal as AI (create, push, read, fromListIO, toListIO) data Purity = PureShared -- ^ conversion is pure even if the original value is shared | PureExclusive -- ^ conversion is pure if the we only convert once deriving (Eq, Ord, Typeable, Data) class PToJSVal a where -- type PureOut a :: Purity pToJSVal :: a -> JSVal class PFromJSVal a where -- type PureIn a :: Purity pFromJSVal :: JSVal -> a class ToJSVal a where toJSVal :: a -> JSM JSVal toJSValListOf :: [a] -> JSM JSVal toJSValListOf = fmap coerce . AI.fromListIO <=< mapM toJSVal default toJSVal :: (Generic a, GToJSVal (Rep a ())) => a -> JSM JSVal toJSVal = toJSVal_generic id class FromJSVal a where fromJSVal :: JSVal -> JSM (Maybe a) fromJSValUnchecked :: JSVal -> JSM a fromJSValUnchecked = fmap fromJust . fromJSVal {-# INLINE fromJSValUnchecked #-} fromJSValListOf :: JSVal -> JSM (Maybe [a]) fromJSValListOf = fmap sequence . (mapM fromJSVal <=< AI.toListIO . coerce) -- fixme should check that it's an array fromJSValUncheckedListOf :: JSVal -> JSM [a] fromJSValUncheckedListOf = mapM fromJSValUnchecked <=< AI.toListIO . coerce default fromJSVal :: (Generic a, GFromJSVal (Rep a ())) => JSVal -> JSM (Maybe a) fromJSVal = fromJSVal_generic id -- ----------------------------------------------------------------------------- class GToJSVal a where gToJSVal :: (String -> String) -> Bool -> a -> JSM JSVal class GToJSProp a where gToJSProp :: (String -> String) -> JSVal -> a -> JSM () class GToJSArr a where gToJSArr :: (String -> String) -> MutableJSArray -> a -> JSM () instance (ToJSVal b) => GToJSVal (K1 a b c) where gToJSVal _ _ (K1 x) = toJSVal x instance GToJSVal p => GToJSVal (Par1 p) where gToJSVal f b (Par1 p) = gToJSVal f b p instance GToJSVal (f p) => GToJSVal (Rec1 f p) where gToJSVal f b (Rec1 x) = gToJSVal f b x instance (GToJSVal (a p), GToJSVal (b p)) => GToJSVal ((a :+: b) p) where gToJSVal f _ (L1 x) = gToJSVal f True x gToJSVal f _ (R1 x) = gToJSVal f True x instance (GToJSVal (a p)) => GToJSVal (M1 D c a p) where gToJSVal f b (M1 x) = gToJSVal f b x instance (Constructor c, GToJSVal (a p)) => GToJSVal (M1 C c a p) where gToJSVal f True m@(M1 x) = do obj@(OI.Object obj') <- OI.create v <- gToJSVal f (conIsRecord m) x OI.setProp (packJSS . f $ conName m) v obj return obj' gToJSVal f _ m@(M1 x) = gToJSVal f (conIsRecord m) x instance (GToJSArr (a p), GToJSArr (b p), GToJSProp (a p), GToJSProp (b p)) => GToJSVal ((a :*: b) p) where gToJSVal f True xy = do (OI.Object obj') <- OI.create gToJSProp f obj' xy return obj' gToJSVal f False xy = do arr@(SomeJSArray arr') <- AI.create gToJSArr f arr xy return arr' instance GToJSVal (a p) => GToJSVal (M1 S c a p) where gToJSVal f b (M1 x) = gToJSVal f b x instance (GToJSProp (a p), GToJSProp (b p)) => GToJSProp ((a :*: b) p) where gToJSProp f o (x :*: y) = gToJSProp f o x >> gToJSProp f o y instance (Selector c, GToJSVal (a p)) => GToJSProp (M1 S c a p) where gToJSProp f o m@(M1 x) = do r <- gToJSVal f False x OI.setProp (packJSS . f $ selName m) r (OI.Object o) instance (GToJSArr (a p), GToJSArr (b p)) => GToJSArr ((a :*: b) p) where gToJSArr f a (x :*: y) = gToJSArr f a x >> gToJSArr f a y instance GToJSVal (a p) => GToJSArr (M1 S c a p) where gToJSArr f a (M1 x) = do r <- gToJSVal f False x AI.push r a instance GToJSVal (V1 p) where gToJSVal _ _ _ = return jsNull instance GToJSVal (U1 p) where gToJSVal _ _ _ = return jsTrue toJSVal_generic :: forall a . (Generic a, GToJSVal (Rep a ())) => (String -> String) -> a -> JSM JSVal toJSVal_generic f x = gToJSVal f False (from x :: Rep a ()) -- ----------------------------------------------------------------------------- class GFromJSVal a where gFromJSVal :: (String -> String) -> Bool -> JSVal -> JSM (Maybe a) class GFromJSProp a where gFromJSProp :: (String -> String) -> JSVal -> JSM (Maybe a) class GFromJSArr a where gFromJSArr :: (String -> String) -> MutableJSArray -> Int -> JSM (Maybe (a,Int)) instance FromJSVal b => GFromJSVal (K1 a b c) where gFromJSVal _ _ r = fmap K1 <$> fromJSVal r instance GFromJSVal p => GFromJSVal (Par1 p) where gFromJSVal f b r = gFromJSVal f b r instance GFromJSVal (f p) => GFromJSVal (Rec1 f p) where gFromJSVal f b r = gFromJSVal f b r instance (GFromJSVal (a p), GFromJSVal (b p)) => GFromJSVal ((a :+: b) p) where gFromJSVal f _b r = do l <- gFromJSVal f True r case l of Just x -> return (L1 <$> Just x) Nothing -> fmap R1 <$> gFromJSVal f True r instance GFromJSVal (a p) => GFromJSVal (M1 D c a p) where gFromJSVal f b r = fmap M1 <$> gFromJSVal f b r instance forall c a p . (Constructor c, GFromJSVal (a p)) => GFromJSVal (M1 C c a p) where gFromJSVal f True r = do r' <- OI.getProp (packJSS . f $ conName (undefined :: M1 C c a p)) (OI.Object r) isUndefinedIO r' >>= \case True -> return Nothing False -> fmap M1 <$> gFromJSVal f (conIsRecord (undefined :: M1 C c a p)) r' gFromJSVal f _ r = fmap M1 <$> gFromJSVal f (conIsRecord (undefined :: M1 C c a p)) r instance (GFromJSArr (a p), GFromJSArr (b p), GFromJSProp (a p), GFromJSProp (b p)) => GFromJSVal ((a :*: b) p) where gFromJSVal f True r = gFromJSProp f r gFromJSVal f False r = fmap fst <$> gFromJSArr f (SomeJSArray r) 0 instance GFromJSVal (a p) => GFromJSVal (M1 S c a p) where gFromJSVal f b r = fmap M1 <$> gFromJSVal f b r instance (GFromJSProp (a p), GFromJSProp (b p)) => GFromJSProp ((a :*: b) p) where gFromJSProp f r = do a <- gFromJSProp f r case a of Nothing -> return Nothing Just a' -> fmap (a':*:) <$> gFromJSProp f r instance forall c a p . (Selector c, GFromJSVal (a p)) => GFromJSProp (M1 S c a p) where gFromJSProp f o = do p <- OI.getProp (packJSS . f $ selName (undefined :: M1 S c a p)) (OI.Object o) isUndefinedIO p >>= \case True -> return Nothing False -> fmap M1 <$> gFromJSVal f False p instance (GFromJSArr (a p), GFromJSArr (b p)) => GFromJSArr ((a :*: b) p) where gFromJSArr f r _n = do a <- gFromJSArr f r 0 case a of Just (a',an) -> do b <- gFromJSArr f r an case b of Just (b',bn) -> return (Just (a' :*: b',bn)) _ -> return Nothing instance (GFromJSVal (a p)) => GFromJSArr (M1 S c a p) where gFromJSArr f o n = do r <- AI.read n o isUndefinedIO r >>= \case True -> return Nothing False -> fmap ((,n+1) . M1) <$> gFromJSVal f False r instance GFromJSVal (V1 p) where gFromJSVal _ _ _ = return Nothing instance GFromJSVal (U1 p) where gFromJSVal _ _ _ = return (Just U1) fromJSVal_generic :: forall a . (Generic a, GFromJSVal (Rep a ())) => (String -> String) -> JSVal -> JSM (Maybe a) fromJSVal_generic f x = fmap to <$> (gFromJSVal f False x :: JSM (Maybe (Rep a ()))) -- ----------------------------------------------------------------------------- fromJSVal_pure :: PFromJSVal a => JSVal -> JSM (Maybe a) fromJSVal_pure x = return (Just (pFromJSVal x)) {-# INLINE fromJSVal_pure #-} fromJSValUnchecked_pure :: PFromJSVal a => JSVal -> JSM a fromJSValUnchecked_pure x = return (pFromJSVal x) {-# INLINE fromJSValUnchecked_pure #-} toJSVal_pure :: PToJSVal a => a -> JSM JSVal toJSVal_pure x = return (pToJSVal x) {-# INLINE toJSVal_pure #-} -- ----------------------------------------------------------------------------- packJSS :: String -> JSString packJSS = textToStr . T.pack