packages feed

clr-bindings-0.2.0: src/Clr/Bindings/IEnumerable.hs

{-# LANGUAGE GADTs, DataKinds, KindSignatures, PolyKinds, TypeFamilies, TypeOperators, RankNTypes, MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances, FlexibleContexts, ScopedTypeVariables, ExistentialQuantification #-}
{-# LANGUAGE UndecidableInstances, TypeApplications, AllowAmbiguousTypes, TypeInType, TypeFamilyDependencies, FunctionalDependencies #-}


module Clr.Bindings.IEnumerable where

import Clr
import Clr.Bridge
import Clr.Resolver
import Clr.TypeString

import Clr.Marshal

import Clr.Host
import Clr.Host.BStr
import Clr.Host.GCHandle

import Clr.Bindings.BStr
import Clr.Bindings.DynImports
import Clr.Bindings.Object

import Data.Kind
import Data.Type.Bool
import Data.Type.Equality
import Foreign.Ptr
import GHC.TypeLits

import Pipes
import qualified Pipes.Prelude

type T_IEnumerable t = T "System.Collections.Generic.IEnumerable" '[t]
type T_IEnumerable'  = T "System.Collections.IEnumerable" '[]
type T_IEnumerator t = T "System.Collections.Generic.IEnumerator" '[t]
type T_IEnumerator'  = T "System.Collections.IEnumerator" '[]

type T_Current       = T "Current" '[]
type T_MoveNext      = T "MoveNext" '[]
type T_GetEnumerator = T "GetEnumerator" '[]

type instance Members (T_IEnumerable t) = '[ T_GetEnumerator ]
type instance Members (T_IEnumerator t) = '[ T_Current ]
type instance Members  T_IEnumerator'   = '[ T_Current, T_MoveNext ]

type instance Candidates (T_IEnumerable t) T_GetEnumerator = '[ '[] ]
type instance Candidates  T_IEnumerator'   T_MoveNext      = '[ '[] ]

type instance SuperTypes (T_IEnumerable t) = '[ T_IEnumerable', T_object ]
type instance SuperTypes (T_IEnumerator t) = '[ T_IEnumerator', T_object ]

foreign import ccall "dynamic" makeGetEnumerator      :: FunPtr (GCHandle a -> IO (GCHandle b)) -> (GCHandle a -> IO (GCHandle b))
foreign import ccall "dynamic" makeEnumeratorMoveNext :: FunPtr (GCHandle (T_IEnumerator') -> IO Bool) -> (GCHandle (T_IEnumerator') -> IO Bool)

foreign import ccall "dynamic" makeEnumeratorCurrentBStr  :: FunPtr (GCHandle (T_IEnumerator elem) -> IO BStr) -> (GCHandle (T_IEnumerator elem) -> IO BStr)
foreign import ccall "dynamic" makeEnumeratorCurrentBool  :: FunPtr (GCHandle (T_IEnumerator elem) -> IO Bool) -> (GCHandle (T_IEnumerator elem) -> IO Bool)
-- TODO: makeEnumCurrent_ for every other prim type
foreign import ccall "dynamic" makeEnumeratorCurrentObj   :: FunPtr (GCHandle (T_IEnumerator elem) -> IO (GCHandle elem)) -> (GCHandle (T_IEnumerator elem) -> IO (GCHandle elem))

instance MethodResultI1 (T_IEnumerable t) (T_GetEnumerator) () where
  type ResultTypeI1 (T_IEnumerable t) (T_GetEnumerator) () = (T_IEnumerator t)

instance MethodDynImportI1 (T_IEnumerable t) (T_GetEnumerator) () where
  methodDynImportI1 = makeGetEnumerator

instance MethodResultI1 T_IEnumerator' T_MoveNext () where
  type ResultTypeI1 T_IEnumerator' T_MoveNext () = T_bool

instance MethodDynImportI1 T_IEnumerator' T_MoveNext () where
  methodDynImportI1 = makeEnumeratorMoveNext

instance PropertyI (T_IEnumerator t) T_Current where
  type PropertyTypeI (T_IEnumerator t) T_Current = t

instance {-# OVERLAPS #-} PropertyDynImportGetI (T_IEnumerator T_string) T_Current where
  propertyDynImportGetI = makeEnumeratorCurrentBStr

instance {-# OVERLAPS #-} PropertyDynImportGetI (T_IEnumerator T_bool) T_Current where
  propertyDynImportGetI = makeEnumeratorCurrentBool
--
-- TODO: PropertyDynImportGetI for every other prim type
--
instance {-# OVERLAPS #-} (IsPrimType (T name gt) ~ 'False) => PropertyDynImportGetI (T_IEnumerator (T name gt)) T_Current where
  propertyDynImportGetI = makeEnumeratorCurrentObj


type family IEnumElemT (x::Type) :: Type where
  IEnumElemT t = IEnumElemT' (WithAllSuperTypes t)

type family IEnumElemT' (x::[Type]) :: Type where
  IEnumElemT'        '[]                                                   = TypeError (Text "Not an instance of System.Collections.Generic.IEnumerable")
  IEnumElemT' ((T "System.Collections.Generic.IEnumerable" '[elem]) ': xs) = elem
  IEnumElemT'     (x ': xs)                                                = IEnumElemT' xs

getEnumerator :: forall t elem . ((t `Implements` (T_IEnumerable elem)) ~ 'True, IEnumElemT t ~ elem, TString elem) => Object t -> IO (Object (T_IEnumerator elem))
getEnumerator x = getEnumerator' $ upCast x
  where getEnumerator' :: Object (T_IEnumerable elem) -> IO (Object (T_IEnumerator elem))
        getEnumerator' x = invokeI @T_GetEnumerator x ()

ienumCurrent :: forall elem propertyBridge propertyHask .
  ( PropertyGetI (T_IEnumerator elem) T_Current
  , BridgeType (PropertyTypeI (T_IEnumerator elem) T_Current) ~ propertyBridge
  , Unmarshal propertyBridge propertyHask
  ) => Object (T_IEnumerator elem) -> IO propertyHask
ienumCurrent ienum = getPropI @T_Current ienum

ienumMoveNext :: forall elem . Object (T_IEnumerator elem) -> IO Bool
ienumMoveNext ienum = invokeI @T_MoveNext ienum ()

toProducer :: forall t elem elemBridge elemHask .
  ( (t `Implements` (T_IEnumerable elem)) ~ 'True
  , IEnumElemT t ~ elem
  , TString elem
  , PropertyGetI (T_IEnumerator elem) T_Current
  , BridgeType (PropertyTypeI (T_IEnumerator elem) T_Current) ~ elemBridge
  , Unmarshal elemBridge elemHask
  ) => Object t -> Producer elemHask IO ()
toProducer ienumerable = do
  ienumerator <- liftIO $ getEnumerator ienumerable
  toProducer' ienumerator

toProducer' :: forall elem elemBridge elemHask .
  ( PropertyGetI (T_IEnumerator elem) T_Current
  , BridgeType (PropertyTypeI (T_IEnumerator elem) T_Current) ~ elemBridge
  , Unmarshal elemBridge elemHask
  ) => Object (T_IEnumerator elem) -> Producer elemHask IO ()
toProducer' ienumerator = do
  nxt <- liftIO $ ienumMoveNext ienumerator
  if nxt then do
    cur <- liftIO $ ienumCurrent ienumerator
    yield cur
    toProducer' ienumerator
  else return ()

toListM :: forall t elem elemBridge elemHask .
  ( (t `Implements` (T_IEnumerable elem)) ~ 'True
  , IEnumElemT t ~ elem
  , TString elem
  , PropertyGetI (T_IEnumerator elem) T_Current
  , BridgeType (PropertyTypeI (T_IEnumerator elem) T_Current) ~ elemBridge
  , Unmarshal elemBridge elemHask
  ) => Object t -> IO [elemHask]
toListM ienumerable = Pipes.Prelude.toListM $ toProducer ienumerable