-- | Module: Internal.Rpc.Breaker
--
-- This module serves to break a dependency cycle between the rpc
-- system and the serialization code; see Note [Breaker] in
-- "Capnp.Rpc.Untyped" for details.
module Internal.Rpc.Breaker
  ( Client (..),
    Pipeline (..),
    nullClient,
    invalidClient,
    -- | ** Internals
    Opaque,
    makeOpaque,
    reflectOpaque,
  )
where

import Data.Dynamic (Dynamic, Typeable, fromDynamic, toDyn)

-- | A reference to a capability, which may be live either in the current vat
-- or elsewhere. Holding a client affords making method calls on a capability
-- or modifying the local vat's reference count to it.
newtype Client = Client Opaque
  deriving (Client -> Client -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Client -> Client -> Bool
$c/= :: Client -> Client -> Bool
== :: Client -> Client -> Bool
$c== :: Client -> Client -> Bool
Eq)

instance Show Client where
  show :: Client -> String
show client :: Client
client@(Client Opaque
opaque) =
    if Client
client forall a. Eq a => a -> a -> Bool
== Client
nullClient
      then String
"nullClient"
      else case forall a. Typeable a => Dynamic -> Maybe a
fromDynamic (Opaque -> Dynamic
reflectOpaque Opaque
opaque) of
        Just (InvalidClient String
errMsg) -> String
"(invalidClient" forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show String
errMsg forall a. [a] -> [a] -> [a]
++ String
")"
        Maybe InvalidClient
Nothing -> String
"({- capability; not statically representable -})"

-- | A 'Pipeline' is a reference to a value within a message that has not yet arrived.
newtype Pipeline = Pipeline Opaque

-- | A null client. This is the only client value that can be represented
-- statically. Throws exceptions in response to all method calls.
nullClient :: Client
nullClient :: Client
nullClient = Opaque -> Client
Client forall a b. (a -> b) -> a -> b
$ forall a. (Typeable a, Eq a) => a -> Opaque
makeOpaque ()

newtype InvalidClient = InvalidClient String
  deriving (Int -> InvalidClient -> ShowS
[InvalidClient] -> ShowS
InvalidClient -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [InvalidClient] -> ShowS
$cshowList :: [InvalidClient] -> ShowS
show :: InvalidClient -> String
$cshow :: InvalidClient -> String
showsPrec :: Int -> InvalidClient -> ShowS
$cshowsPrec :: Int -> InvalidClient -> ShowS
Show, ReadPrec [InvalidClient]
ReadPrec InvalidClient
Int -> ReadS InvalidClient
ReadS [InvalidClient]
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [InvalidClient]
$creadListPrec :: ReadPrec [InvalidClient]
readPrec :: ReadPrec InvalidClient
$creadPrec :: ReadPrec InvalidClient
readList :: ReadS [InvalidClient]
$creadList :: ReadS [InvalidClient]
readsPrec :: Int -> ReadS InvalidClient
$creadsPrec :: Int -> ReadS InvalidClient
Read, InvalidClient -> InvalidClient -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: InvalidClient -> InvalidClient -> Bool
$c/= :: InvalidClient -> InvalidClient -> Bool
== :: InvalidClient -> InvalidClient -> Bool
$c== :: InvalidClient -> InvalidClient -> Bool
Eq, Typeable)

-- | Returns a client which is "invalid;" it behaves like 'nullClient',
-- but can be given a custom error message that is displayed by 'show'.
invalidClient :: String -> Client
invalidClient :: String -> Client
invalidClient = Opaque -> Client
Client forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. (Typeable a, Eq a) => a -> Opaque
makeOpaque forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> InvalidClient
InvalidClient

data Opaque = Opaque
  { Opaque -> Dynamic
opDyn :: Dynamic,
    Opaque -> Opaque -> Bool
opEq :: Opaque -> Bool
  }

makeOpaque :: (Typeable a, Eq a) => a -> Opaque
makeOpaque :: forall a. (Typeable a, Eq a) => a -> Opaque
makeOpaque a
v =
  Opaque
    { opDyn :: Dynamic
opDyn = forall a. Typeable a => a -> Dynamic
toDyn a
v,
      opEq :: Opaque -> Bool
opEq = \Opaque
o -> forall a. Typeable a => Dynamic -> Maybe a
fromDynamic (Opaque -> Dynamic
opDyn Opaque
o) forall a. Eq a => a -> a -> Bool
== forall a. a -> Maybe a
Just a
v
    }

reflectOpaque :: Opaque -> Dynamic
reflectOpaque :: Opaque -> Dynamic
reflectOpaque = Opaque -> Dynamic
opDyn

instance Eq Opaque where
  Opaque
x == :: Opaque -> Opaque -> Bool
== Opaque
y = Opaque -> Opaque -> Bool
opEq Opaque
x Opaque
y