rPC.ml1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190module Uint32 = Stdint.Uint32 module Uint64 = Stdint.Uint64 module Registry : sig (** Handy central registry of all known interfaces, for logging. *) (** Used in the generated code to register the interfaces. *) val register : interface_id:Uint64.t -> name:string -> (int -> string option) -> unit (** [pp_method] is a formatter for [(interface_id, method_id)] pairs. It prints out qualified names, suitable for logging (e.g. "Foo.bar") *) val pp_method : Format.formatter -> Uint64.t * int -> unit val pp_interface : Format.formatter -> Uint64.t -> unit end = struct type interface = { name : string; method_lookup : int -> string option; } let interfaces = Hashtbl.create 7 let register ~interface_id ~name method_lookup = Hashtbl.add interfaces interface_id {name; method_lookup} let pp_method f (interface_id, method_id) = match Hashtbl.find interfaces interface_id with | exception Not_found -> Format.fprintf f "<interface %a>.<method-%d>" Uint64.printer interface_id method_id | interface -> match interface.method_lookup method_id with | Some method_name -> Format.fprintf f "%s.%s" interface.name method_name | None -> Format.fprintf f "%s.<method-%d>" interface.name method_id let pp_interface f interface_id = match Hashtbl.find interfaces interface_id with | exception Not_found -> Format.fprintf f "<interface %a>" Uint64.printer interface_id | interface -> Format.fprintf f "%s" interface.name end module MethodID : sig (** A globally unique method ID, for a method on the interface ['interface], which takes parameters of type ['request] and produces results of type ['response]. *) type ('interface, 'request, 'response) t val v : interface_id:Uint64.t -> method_id:int -> ('interface, 'req, 'resp) t val interface_id : (_, _, _) t -> Uint64.t val method_id : (_, _, _) t -> int val pp : Format.formatter -> (_, _, _) t -> unit end = struct type ('interface, 'request, 'response) t = Uint64.t * int let v ~interface_id ~method_id = (interface_id, method_id) let interface_id : (_, _, _) t -> Uint64.t = fst let method_id : (_, _, _) t -> int = snd let pp t = Registry.pp_method t end module type S = sig (** Extends [MessageSig.S] with types for RPC. *) include MessageSig.S module Service : sig (** The type of a method provided by the server application code. This is used in the generated code for the service class type. *) type ('a, 'b) method_t end module StructRef : sig (** A reference to a struct, which may not have arrived yet. *) type 'a t end module Capability : sig (** A reference to an interface, which may be remote. *) type 'a t end module Untyped : sig (** This module is only for use by the code generated by the capnp-ocaml schema compiler. The generated code provides type-safe wrappers for everything here. *) (** An untyped method. This will typically be something like ['a reader_t -> 'b StructRef.t]. i.e. the result of calling an interface's method is a promise for the future result. *) type abstract_method_t (** Cast a method to [abstract_method_t]. Typically this will be the identity function. This is used in the generated code to ensure that all methods have the same type for the dispatch function. *) val abstract_method : ('a StructStorage.reader_t, 'b) Service.method_t -> abstract_method_t (** [struct_field t i] is a reference to the struct found at pointer index [i] within the struct [t]. Used to implement the "_pipelined" accessors. *) val struct_field : 'a StructRef.t -> int -> 'b StructRef.t (** [capability_field t i] is a reference to the capability found at pointer index [i] within the struct [t]. Used to implement the "_pipelined" accessors. *) val capability_field : 'a StructRef.t -> int -> 'b Capability.t class type generic_service = object method dispatch : interface_id:Uint64.t -> method_id:int -> abstract_method_t (** Look up a method by ID. The schema compiler generates an implementation of this that dispatches to the typed methods of the interface. *) method release : unit (** Called when the service's ref-count drops to zero. Implementations that hold other capabilities should override this to release them in turn. *) method pp : Format.formatter -> unit (** Used to identify the service in log messages. The schema compiler generates a default that displays the service's name. *) end (** [local service] is a capability reference to a local service implemented by [service#dispatch]. Used by the generated functions with the same name (but a fixed type). *) val local : #generic_service -> 'a Capability.t (** Used in the generated code to get a capability from the attachments by index. *) val get_cap : MessageSig.attachments -> Uint32.t -> 'a Capability.t (** Used in the generated code to store a capability in the attachments. Returns the new index. *) val add_cap : MessageSig.attachments -> 'a Capability.t -> Uint32.t (** Remove a capability from the attachments. Used if the interface is changed. *) val clear_cap : MessageSig.attachments -> Uint32.t -> unit (** Used to handle calls when the interface ID isn't known. *) val unknown_interface : interface_id:Uint64.t -> abstract_method_t (** Used to handle calls when the method ID isn't known. *) val unknown_method : interface_id:Uint64.t -> method_id:int -> abstract_method_t end end module None (M : MessageSig.S) = struct (** A dummy RPC provider, for when the RPC features (interfaces) aren't needed. *) include M module Untyped = struct type untyped_struct = [`No_RPC_struct] type abstract_method_t = [`No_RPC_payload] -> untyped_struct let define_method ~interface_id ~method_id = (interface_id, method_id) let abstract_method x = x let struct_field `No_RPC_struct _ = `No_RPC_struct let capability_field `No_RPC_struct _ = failwith "Can't pipeline with RPC.None!" let local _ = failwith "Can't use local with RPC.None!" let get_cap _ i = i let add_cap _ i = i let clear_cap _ _ = () let unknown_interface ~interface_id:_ _req = failwith "Unknown interface" let unknown_method ~interface_id:_ ~method_id:_ _req = failwith "Unknown method" class type generic_service = object method dispatch : interface_id:Uint64.t -> method_id:int -> abstract_method_t method release : unit method pp : Format.formatter -> unit end end module StructRef = struct type 'a t = Untyped.untyped_struct end module Capability = struct type 'a t = Uint32.t (* Just the raw CapDescriptor table index. *) end module Service = struct type ('a, 'b) method_t = Untyped.abstract_method_t end end