{-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} -- | Module: Capnp.Fields -- Description: Support for working with struct fields module Capnp.Fields ( HasField(..) , Field(..) , FieldLoc(..) , DataFieldLoc(..) , FieldKind(..) , HasUnion(..) , Variant(..) , HasVariant(..) ) where import Capnp.Bits import Data.Word import GHC.OverloadedLabels (IsLabel(..)) import GHC.TypeLits (Symbol) import qualified Capnp.Classes as C import qualified Capnp.Message as M import qualified Capnp.New.Classes as NC import qualified Capnp.Repr as R import qualified Capnp.Untyped as U -- | What sort of field is this? This corresponds to the slot/group variants -- in the @Field@ type in schema.capnp. Mostly used at the type level with -- the @DataKinds@ extension. -- -- (Note that this has nothing to do with kinds in the usual type system sense -- of the word). data FieldKind = Slot -- ^ The field is a normal slot; it can be read and written as an -- individual value. | Group -- ^ The field is a group. Since this shares space with its parent struct -- access patterns are a bit different. deriving(Show, Read, Eq) -- | @'Field' k a b@ is a first-class representation of a field of type @b@ within -- an @a@, where @a@ must be a struct type. newtype Field (k :: FieldKind) a b = Field (FieldLoc k (R.ReprFor b)) -- | The location of a field within a message. data FieldLoc (k :: FieldKind) (r :: R.Repr) where GroupField :: FieldLoc 'Group ('R.Ptr ('Just 'R.Struct)) PtrField :: R.IsPtrRepr a => Word16 -> FieldLoc 'Slot ('R.Ptr a) DataField :: C.IsWord (R.UntypedData a) => DataFieldLoc a -> FieldLoc 'Slot ('R.Data a) VoidField :: FieldLoc 'Slot ('R.Data 'R.Sz0) -- | The location of a data (non-pointer) field. data DataFieldLoc (sz :: R.DataSz) = DataFieldLoc { shift :: !BitCount , index :: !Word16 , mask :: !Word64 , defaultValue :: !Word64 } -- | An instance of 'HasUnion' indicates that the given type is a capnproto struct -- (or group) with an anonymous union. class R.IsStruct a => HasUnion a where -- | 'unionField' is a field holding the union's tag. unionField :: Field 'Slot a Word16 -- | 'Which' is the abstract capnproto type of the union itself. Like -- generated struct types (in this case @a@), this is typically -- uninhabitied, and used to define instances and/or act as a phantom type. data Which a -- | Concrete view into a union embedded in a message. This will be a sum -- type with other 'Raw' values as arguments. data RawWhich (mut :: M.Mutability) a -- | Helper used in generated code to extract a 'RawWhich' from its -- surrounding struct. internalWhich :: U.ReadCtx m mut => Word16 -> R.Raw mut a -> m (RawWhich mut a) type instance R.ReprFor (Which a) = 'R.Ptr ('Just 'R.Struct) instance (NC.Allocate a, HasUnion a, R.IsStruct (Which a)) => NC.Allocate (Which a) where type AllocHint (Which a) = NC.AllocHint a new hint msg = do R.Raw struct <- NC.new @a hint msg pure (R.Raw struct) instance ( NC.Allocate (Which a) , NC.AllocHint (Which a) ~ () , NC.Parse (Which a) p ) => NC.EstimateAlloc (Which a) p -- | @'Variant' k a b@ is a first-class representation of a variant of @a@'s -- anonymous union, whose argument is of type @b@. data Variant (k :: FieldKind) a b = Variant { field :: !(Field k a b) , tagValue :: !Word16 } -- | An instance @'HasField' name k a b@ indicates that the struct type @a@ -- has a field named @name@ with type @b@ (with @k@ being the 'FieldKind' for -- the field). The generated code includes instances of this for each field -- in the schema. class R.IsStruct a => HasField (name :: Symbol) k a b | a name -> k b where fieldByLabel :: Field k a b instance HasField name k a b => IsLabel name (Field k a b) where fromLabel = fieldByLabel @name @k @a @b -- | An instance @'HasVariant name k a b@ indicates that the struct type @a@ -- has an anonymous union with a variant named @name@, whose argument is of type -- @b@. class HasUnion a => HasVariant (name :: Symbol) k a b | a name -> k b where variantByLabel :: Variant k a b instance HasVariant name k a b => IsLabel name (Variant k a b) where fromLabel = variantByLabel @name @k @a @b