hsqml-0.3.6.0: src/Graphics/QML/Marshal.hs
{-# LANGUAGE
ScopedTypeVariables,
TypeFamilies,
FlexibleInstances
#-}
{-# OPTIONS_GHC -Wno-orphans #-}
-- | Type classs and instances for marshalling values between Haskell and QML.
module Graphics.QML.Marshal (
-- * Marshalling Type-class
Marshal (
type MarshalMode,
marshaller),
ModeBidi,
ModeFrom,
ModeTo,
ModeRetVoid,
ModeObjBidi,
ModeObjFrom,
ModeObjTo,
Yes,
CanGetFrom,
ICanGetFrom,
CanPassTo,
ICanPassTo,
CanReturnTo,
ICanReturnTo,
IsObjType,
IIsObjType,
GetObjType,
IGetObjType,
Marshaller,
-- * Data Types
Ignored (
Ignored),
-- * Custom Marshallers
bidiMarshallerIO,
bidiMarshaller,
fromMarshallerIO,
fromMarshaller,
toMarshallerIO,
toMarshaller
) where
import Graphics.QML.Internal.BindPrim
import Graphics.QML.Internal.Marshal
import Graphics.QML.Internal.Types
import Control.Monad
import Control.Monad.Trans.Maybe
import qualified Data.ByteString as BS
import qualified Data.ByteString.Unsafe as BSU
import Data.Tagged
import Data.Int
import Data.Text (Text)
import qualified Data.Text.Encoding as TE
import Foreign.C.Types
import Foreign.Marshal.Alloc
import Foreign.Marshal.Utils (copyBytes)
import Foreign.Ptr
import Foreign.Storable
--
-- Boolean built-in type
--
instance Marshal Bool where
type MarshalMode Bool c d = ModeBidi c
marshaller = Marshaller {
mTypeCVal_ = Tagged tyJSValue,
mFromCVal_ = jvalFromCVal,
mToCVal_ = jvalToCVal,
mWithCVal_ = jvalWithCVal,
mFromJVal_ = \s ptr ->
MaybeT $ fromJVal s hsqmlIsJvalBool hsqmlGetJvalBool ptr,
mWithJVal_ = \bool f ->
withJVal hsqmlInitJvalBool bool f,
mFromHndl_ = unimplFromHndl,
mToHndl_ = unimplToHndl}
--
-- Int32/int built-in type
--
instance Marshal Int32 where
type MarshalMode Int32 c d = ModeBidi c
marshaller = Marshaller {
mTypeCVal_ = Tagged tyInt,
mFromCVal_ = \ptr ->
errIO $ peek (castPtr ptr :: Ptr CInt) >>= return . fromIntegral,
mToCVal_ = \int ptr ->
poke (castPtr ptr :: Ptr CInt) (fromIntegral int),
mWithCVal_ = \int f ->
alloca $ \(ptr :: Ptr CInt) ->
mToCVal int (castPtr ptr) >> f (castPtr ptr),
mFromJVal_ = \s ptr ->
MaybeT $ fromJVal s hsqmlIsJvalNumber (
fmap fromIntegral . hsqmlGetJvalInt) ptr,
mWithJVal_ = \int f ->
withJVal hsqmlInitJvalInt (fromIntegral int) f,
mFromHndl_ = unimplFromHndl,
mToHndl_ = unimplToHndl}
instance Marshal Int where
type MarshalMode Int c d = ModeBidi c
marshaller = Marshaller {
mTypeCVal_ = Tagged tyInt,
mFromCVal_ = fmap (fromIntegral :: Int32 -> Int) . mFromCVal,
mToCVal_ = \int ptr -> mToCVal (fromIntegral int :: Int32) ptr,
mWithCVal_ = \int f -> mWithCVal (fromIntegral int :: Int32) f,
mFromJVal_ = \s -> fmap (fromIntegral :: Int32 -> Int) . mFromJVal s,
mWithJVal_ = \int f -> mWithJVal (fromIntegral int :: Int32) f,
mFromHndl_ = unimplFromHndl,
mToHndl_ = unimplToHndl}
--
-- Double/double built-in type
--
instance Marshal Double where
type MarshalMode Double c d = ModeBidi c
marshaller = Marshaller {
mTypeCVal_ = Tagged tyDouble,
mFromCVal_ = \ptr ->
errIO $ peek (castPtr ptr :: Ptr CDouble) >>= return . realToFrac,
mToCVal_ = \num ptr ->
poke (castPtr ptr :: Ptr CDouble) (realToFrac num),
mWithCVal_ = \num f ->
alloca $ \(ptr :: Ptr CDouble) ->
mToCVal num (castPtr ptr) >> f (castPtr ptr),
mFromJVal_ = \s ptr ->
MaybeT $ fromJVal s hsqmlIsJvalNumber (
fmap realToFrac . hsqmlGetJvalDouble) ptr,
mWithJVal_ = \num f ->
withJVal hsqmlInitJvalDouble (realToFrac num) f,
mFromHndl_ = unimplFromHndl,
mToHndl_ = unimplToHndl}
--
-- Text/QString built-in type
--
instance Marshal Text where
type MarshalMode Text c d = ModeBidi c
marshaller = Marshaller {
mTypeCVal_ = Tagged tyString,
mFromCVal_ = \ptr -> errIO $ do
pair <- alloca (\bufPtr -> do
len <- hsqmlReadString (HsQMLStringHandle $ castPtr ptr) bufPtr
buf <- peek bufPtr
return (buf, len))
let (buf, len) = pair
bs <- BS.packCStringLen (castPtr buf, len * 2)
return $ TE.decodeUtf16LE bs,
mToCVal_ = \txt ptr -> do
let bs = TE.encodeUtf16LE txt
ucs2Length = BS.length bs `div` 2
array <- hsqmlWriteString ucs2Length (HsQMLStringHandle $ castPtr ptr)
BSU.unsafeUseAsCStringLen bs (\(bsp, bslen) -> copyBytes (castPtr array) bsp bslen),
mWithCVal_ = \txt f ->
withStrHndl $ \(HsQMLStringHandle ptr) -> do
mToCVal txt $ castPtr ptr
f $ castPtr ptr,
mFromJVal_ = \s jval ->
MaybeT $ withStrHndl $ \sHndl -> runMaybeT $ do
MaybeT $ fromJVal s hsqmlIsJvalString (
flip hsqmlGetJvalString sHndl) jval
let (HsQMLStringHandle ptr) = sHndl
mFromCVal $ castPtr ptr,
mWithJVal_ = \txt f ->
mWithCVal txt $ \ptr -> withJVal hsqmlInitJvalString (
HsQMLStringHandle $ castPtr ptr) f,
mFromHndl_ = unimplFromHndl,
mToHndl_ = unimplToHndl}
--
-- Maybe
--
instance (Marshal a) => Marshal (Maybe a) where
type MarshalMode (Maybe a) ICanGetFrom d = MarshalMode a ICanGetFrom d
type MarshalMode (Maybe a) ICanPassTo d = MarshalMode a ICanPassTo d
type MarshalMode (Maybe a) ICanReturnTo d = MarshalMode a ICanReturnTo d
type MarshalMode (Maybe a) IIsObjType d = No
type MarshalMode (Maybe a) IGetObjType d = No
marshaller = Marshaller {
mTypeCVal_ = Tagged tyJSValue,
mFromCVal_ = jvalFromCVal,
mToCVal_ = jvalToCVal,
mWithCVal_ = jvalWithCVal,
mFromJVal_ = \_ jval -> errIO $ runMaybeT $ mFromJVal Weak jval,
mWithJVal_ = \val f ->
case val of
Just val' -> mWithJVal val' f
Nothing -> withJVal hsqmlInitJvalNull False f,
mFromHndl_ = unimplFromHndl,
mToHndl_ = unimplToHndl}
--
-- List
--
instance (Marshal a) => Marshal [a] where
type MarshalMode [a] ICanGetFrom d = MarshalMode a ICanGetFrom d
type MarshalMode [a] ICanPassTo d = MarshalMode a ICanPassTo d
type MarshalMode [a] ICanReturnTo d = MarshalMode a ICanReturnTo d
type MarshalMode [a] IIsObjType d = No
type MarshalMode [a] IGetObjType d = No
marshaller = Marshaller {
mTypeCVal_ = Tagged tyJSValue,
mFromCVal_ = jvalFromCVal,
mToCVal_ = jvalToCVal,
mWithCVal_ = jvalWithCVal,
mFromJVal_ = \s jval -> MaybeT $ do
len <- hsqmlGetJvalArrayLength jval
withJVal hsqmlInitJvalNull True $ \tmp ->
runMaybeT $ forM [0..len-1] $ \i -> do
errIO $ hsqmlJvalArrayGet jval i tmp
mFromJVal s tmp,
mWithJVal_ = \vs f ->
withJVal hsqmlInitJvalArray (length vs) $ \jval -> do
forM_ (zip [0..] vs) $ uncurry $ \i val ->
mWithJVal val $ \jval' ->
hsqmlJvalArraySet jval i jval'
f jval,
mFromHndl_ = unimplFromHndl,
mToHndl_ = unimplToHndl}
--
-- Ignored
--
-- | Represents an argument whose value is ignored.
data Ignored = Ignored
instance Marshal Ignored where
type MarshalMode Ignored c d = ModeFrom c
marshaller = Marshaller {
mTypeCVal_ = Tagged tyJSValue,
mFromCVal_ = jvalFromCVal,
mToCVal_ = unimplToCVal,
mWithCVal_ = unimplWithCVal,
mFromJVal_ = \_ _ -> MaybeT . return $ Just Ignored,
mWithJVal_ = unimplWithJVal,
mFromHndl_ = unimplFromHndl,
mToHndl_ = unimplToHndl}
type BidiMarshaller a b = Marshaller b
(MarshalMode a ICanGetFrom ())
(MarshalMode a ICanPassTo ())
(MarshalMode a ICanReturnTo ())
(MarshalMode a IIsObjType ())
(MarshalMode a IGetObjType ())
-- | Provides a bidirectional 'Marshaller' which allows you to define an
-- instance of 'Marshal' for your own type @b@ in terms of another marshallable
-- type @a@. Type @b@ should have a 'MarshalMode' of 'ModeObjBidi' or
-- 'ModeBidi' depending on whether @a@ was an object type or not.
bidiMarshallerIO ::
forall a b. (Marshal a, CanGetFrom a ~ Yes, CanPassTo a ~ Yes) =>
(a -> IO b) -> (b -> IO a) -> BidiMarshaller a b
bidiMarshallerIO fromFn toFn = Marshaller {
mTypeCVal_ = retag (mTypeCVal :: Tagged a TypeId),
mFromCVal_ = \ptr -> (errIO . fromFn) =<< mFromCVal ptr,
mToCVal_ = \val ptr -> flip mToCVal ptr =<< toFn val,
mWithCVal_ = \val f -> flip mWithCVal f =<< toFn val,
mFromJVal_ = \s ptr -> (errIO . fromFn) =<< mFromJVal s ptr,
mWithJVal_ = \val f -> flip mWithJVal f =<< toFn val,
mFromHndl_ = \hndl -> fromFn =<< mFromHndl hndl,
mToHndl_ = \val -> mToHndl =<< toFn val}
-- | Variant of 'bidiMarshallerIO' where the conversion functions between types
-- @a@ and @b@ do not live in the IO monad.
bidiMarshaller ::
forall a b. (Marshal a, CanGetFrom a ~ Yes, CanPassTo a ~ Yes) =>
(a -> b) -> (b -> a) -> BidiMarshaller a b
bidiMarshaller fromFn toFn =
bidiMarshallerIO (return . fromFn) (return . toFn)
type FromMarshaller a b = Marshaller b
(MarshalMode a ICanGetFrom ())
No
No
(MarshalMode a IIsObjType ())
(MarshalMode a IGetObjType ())
-- | Provides a "from" 'Marshaller' which allows you to define an instance of
-- 'Marshal' for your own type @b@ in terms of another marshallable type @a@.
-- Type @b@ should have a 'MarshalMode' of 'ModeObjFrom' or 'ModeFrom'
-- depending on whether @a@ was an object type or not.
fromMarshallerIO ::
forall a b. (Marshal a, CanGetFrom a ~ Yes) =>
(a -> IO b) -> FromMarshaller a b
fromMarshallerIO fromFn = Marshaller {
mTypeCVal_ = retag (mTypeCVal :: Tagged a TypeId),
mFromCVal_ = \ptr -> (errIO . fromFn) =<< mFromCVal ptr,
mToCVal_ = unimplToCVal,
mWithCVal_ = unimplWithCVal,
mFromJVal_ = \s ptr -> (errIO . fromFn) =<< mFromJVal s ptr,
mWithJVal_ = unimplWithJVal,
mFromHndl_ = \hndl -> fromFn =<< mFromHndl hndl,
mToHndl_ = unimplToHndl}
-- | Variant of 'fromMarshallerIO' where the conversion function between types
-- @a@ and @b@ does not live in the IO monad.
fromMarshaller ::
forall a b. (Marshal a, CanGetFrom a ~ Yes) =>
(a -> b) -> FromMarshaller a b
fromMarshaller fromFn = fromMarshallerIO (return . fromFn)
type ToMarshaller a b = Marshaller b
No
(MarshalMode a ICanPassTo ())
(MarshalMode a ICanReturnTo ())
(MarshalMode a IIsObjType ())
(MarshalMode a IGetObjType ())
-- | Provides a "to" 'Marshaller' which allows you to define an instance of
-- 'Marshal' for your own type @b@ in terms of another marshallable type @a@.
-- Type @b@ should have a 'MarshalMode' of 'ModeObjTo' or 'ModeTo'
-- depending on whether @a@ was an object type or not.
toMarshallerIO ::
forall a b. (Marshal a, CanPassTo a ~ Yes) =>
(b -> IO a) -> ToMarshaller a b
toMarshallerIO toFn = Marshaller {
mTypeCVal_ = retag (mTypeCVal :: Tagged a TypeId),
mFromCVal_ = unimplFromCVal,
mToCVal_ = \val ptr -> flip mToCVal ptr =<< toFn val,
mWithCVal_ = \val f -> flip mWithCVal f =<< toFn val,
mFromJVal_ = unimplFromJVal,
mWithJVal_ = \val f -> flip mWithJVal f =<< toFn val,
mFromHndl_ = unimplFromHndl,
mToHndl_ = \val -> mToHndl =<< toFn val}
-- | Variant of 'toMarshallerIO' where the conversion function between types
-- @a@ and @b@ does not live in the IO monad.
toMarshaller ::
forall a b. (Marshal a, CanPassTo a ~ Yes) =>
(b -> a) -> ToMarshaller a b
toMarshaller toFn = toMarshallerIO (return . toFn)