From 516fb61c4890233a355c700c03b769960ed0a65d Mon Sep 17 00:00:00 2001 From: Mathieu Barbin Date: Mon, 8 Jan 2024 13:54:10 +0100 Subject: [PATCH 1/4] Introducing typed RPC specifications This is extracted from #48 as a standalone change which should allow starting building the other pieces (eio, lwt, async, protoc & protoc_plugin) independently. --- dune-project | 4 ++ grpc.opam | 2 +- lib/grpc/grpc.ml | 1 + lib/grpc/rpc.ml | 47 +++++++++++++++++++ lib/grpc/rpc.mli | 116 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 lib/grpc/rpc.ml create mode 100644 lib/grpc/rpc.mli diff --git a/dune-project b/dune-project index 32c4d3a..715bdfb 100644 --- a/dune-project +++ b/dune-project @@ -38,6 +38,10 @@ (>= 0.9.1)) (h2 (>= 0.9.0)) + (odoc + (and + (>= 2.4.0) + :with-doc)) ppx_deriving (uri (>= 4.0.0)))) diff --git a/grpc.opam b/grpc.opam index 8355be4..a071e05 100644 --- a/grpc.opam +++ b/grpc.opam @@ -23,9 +23,9 @@ depends: [ "ocaml" {>= "4.08"} "bigstringaf" {>= "0.9.1"} "h2" {>= "0.9.0"} + "odoc" {>= "2.4.0" & with-doc} "ppx_deriving" "uri" {>= "4.0.0"} - "odoc" {with-doc} ] build: [ ["dune" "subst"] {dev} diff --git a/lib/grpc/grpc.ml b/lib/grpc/grpc.ml index 00ca697..c84744b 100644 --- a/lib/grpc/grpc.ml +++ b/lib/grpc/grpc.ml @@ -2,3 +2,4 @@ module Server = Server module Status = Status module Message = Message module Buffer = Buffer +module Rpc = Rpc diff --git a/lib/grpc/rpc.ml b/lib/grpc/rpc.ml new file mode 100644 index 0000000..75859ac --- /dev/null +++ b/lib/grpc/rpc.ml @@ -0,0 +1,47 @@ +module Value_mode = struct + type unary + type stream + type _ t = Unary : unary t | Stream : stream t +end + +module Service_spec = struct + type t = { package : string list; service_name : string } + + let packaged_service_name { package; service_name } = + String.concat "." (package @ [ service_name ]) +end + +type buffer = string + +module Client_rpc = struct + type ('request, 'request_mode, 'response, 'response_mode) t = { + service_spec : Service_spec.t; + rpc_name : string; + encode_request : 'request -> buffer; + decode_response : buffer -> 'response; + request_mode : 'request_mode Value_mode.t; + response_mode : 'response_mode Value_mode.t; + } +end + +module Server_rpc = struct + module Service_spec = struct + type 'a t = None : unit t | Some : Service_spec.t -> Service_spec.t t + end + + type ('request, 'request_mode, 'response, 'response_mode, 'service_spec) t = { + service_spec : 'service_spec Service_spec.t; + rpc_name : string; + decode_request : buffer -> 'request; + encode_response : 'response -> buffer; + request_mode : 'request_mode Value_mode.t; + response_mode : 'response_mode Value_mode.t; + } +end + +module Handlers = struct + type ('a, 'b) t = + | Handlers of { handlers : 'a list } + | With_service_spec of { handlers : 'b list; service_spec : Service_spec.t } + | ( :: ) of ('a, 'b) t * ('a, 'b) t list +end diff --git a/lib/grpc/rpc.mli b/lib/grpc/rpc.mli new file mode 100644 index 0000000..3d87b4a --- /dev/null +++ b/lib/grpc/rpc.mli @@ -0,0 +1,116 @@ +(** Creating typed specification for RPCs. + + This module provides the functionality to create typed specifications for + RPCs. It defines abstractions for both client and server sides to utilize + the typed interfaces of Grpc. These abstractions can be constructed + directly by advanced users or via helper libraries for commonly used + protoc providers supported by Grpc. *) + +module Value_mode : sig + (** A type used to differentiate between unary and stream values. + + Grpc supports the definition of RPCs that either take and return a single + value or a stream of values. The table below illustrates the four types of + RPCs that can be defined, distinguished by the {!type:Value_mode.t} of + their [request_mode] and [response_mode] fields. + + {t + | request_mode | response_mode | rpc kind | + | :----------: | :------------:|:-----------------------:| + | Unary | Unary | unary | + | Unary | Stream | server_streaming | + | Stream | Unary | client_streaming | + | Stream | Stream | bidirectional_streaming | + } *) + + type unary + type stream + type _ t = Unary : unary t | Stream : stream t +end + +module Service_spec : sig + (** The complete name used to identify a service. *) + + type t = { package : string list; service_name : string } + (** Services can be qualified by a list of {!field:package} names in addition + to their {!field:service_name}. Values of this type are typically + auto-generated from the service interfaces defined in *.proto files. *) + + val packaged_service_name : t -> string + (** This function constructs a canonical service name that acts as a key to + identify and retrieve the correct service at runtime. The convention is + to concatenate the package and service names, separated by a dot. *) +end + +type buffer = string +(** The {!type:buffer} type represents the messages exchanged by the low-level + transport layer of Grpc. The typed specification includes transformation + functions to convert to and from this wire encoding. Depending on the + specification's construction, this string may represent messages in JSON + or Protobuf format. *) + +(** {1 Client side} *) + +module Client_rpc : sig + (** RPC specification used by clients when calling gRPCs. *) + + type ('request, 'request_mode, 'response, 'response_mode) t = { + service_spec : Service_spec.t; + rpc_name : string; + encode_request : 'request -> buffer; + decode_response : buffer -> 'response; + request_mode : 'request_mode Value_mode.t; + response_mode : 'response_mode Value_mode.t; + } +end + +(** {1 Server side} *) + +module Server_rpc : sig + (** RPC specification used by server when implementing gRPCs. *) + + module Service_spec : sig + (** This type indicates whether a {!Service_spec.t} is available in the + server-side specification. + + Grpc supports several protoc providers (ocaml-protoc & ocaml-protoc-plugin), + which differ in the information available in their server-side handlers. + + {t + | protoc library | service_spec in handler | + | :-----------------: | :----------------------:| + | ocaml-protoc | No | + | ocaml-protoc-plugin | Yes | + } *) + type 'a t = None : unit t | Some : Service_spec.t -> Service_spec.t t + end + + type ('request, 'request_mode, 'response, 'response_mode, 'service_spec) t = { + service_spec : 'service_spec Service_spec.t; + rpc_name : string; + decode_request : buffer -> 'request; + encode_response : 'response -> buffer; + request_mode : 'request_mode Value_mode.t; + response_mode : 'response_mode Value_mode.t; + } +end + +module Handlers : sig + (** This type helps distinguish between server handlers that do or do not + contain the specification of the service they implement. The type is + parameterized as it is shared by libraries that depend on different + concurrency libraries, causing the actual type of handlers to vary. *) + type ('a, 'b) t = + | Handlers of { handlers : 'a list } + (** This representation is used when each handler contains a reference + to the service spec, such as when they are built with + [ocaml_protoc_plugin]. *) + | With_service_spec of { handlers : 'b list; service_spec : Service_spec.t } + (** If the service spec is not represented by each handler, it must be + passed along with all handlers that implement an RPC for that + service. *) + | ( :: ) of ('a, 'b) t * ('a, 'b) t list + (** This constructor allows multiple services' handlers to be + implemented on the same server, supplying them grouped using list + syntax. *) +end From fa74b5b916b948a183a9a175b8ac5eecec40d17d Mon Sep 17 00:00:00 2001 From: Mathieu Barbin Date: Mon, 8 Jan 2024 15:26:26 +0100 Subject: [PATCH 2/4] Add helper library for using typed-rpc with ocaml-protoc-plugin --- dune-project | 11 +++ grpc-protoc-plugin.opam | 40 ++++++++ lib/grpc-protoc-plugin/dune | 4 + lib/grpc-protoc-plugin/grpc_protoc_plugin.ml | 69 ++++++++++++++ lib/grpc-protoc-plugin/grpc_protoc_plugin.mli | 92 +++++++++++++++++++ 5 files changed, 216 insertions(+) create mode 100644 grpc-protoc-plugin.opam create mode 100644 lib/grpc-protoc-plugin/dune create mode 100644 lib/grpc-protoc-plugin/grpc_protoc_plugin.ml create mode 100644 lib/grpc-protoc-plugin/grpc_protoc_plugin.mli diff --git a/dune-project b/dune-project index 715bdfb..8dd7965 100644 --- a/dune-project +++ b/dune-project @@ -96,6 +96,17 @@ (>= 0.9.0)) stringext)) +(package + (name grpc-protoc-plugin) + (synopsis "An implementation of gRPC using ocaml-protoc-plugin") + (description + "Functionality for building gRPC services and rpcs with `ocaml-protoc-plugin`") + (depends + (grpc + (= :version)) + (ocaml-protoc-plugin + (>= 4.5)))) + (package (name grpc-examples) (synopsis "Various gRPC examples") diff --git a/grpc-protoc-plugin.opam b/grpc-protoc-plugin.opam new file mode 100644 index 0000000..1ddae22 --- /dev/null +++ b/grpc-protoc-plugin.opam @@ -0,0 +1,40 @@ +# This file is generated by dune, edit dune-project instead +opam-version: "2.0" +synopsis: "An implementation of gRPC using ocaml-protoc-plugin" +description: + "Functionality for building gRPC services and rpcs with `ocaml-protoc-plugin`" +maintainer: ["Daniel Quernheim "] +authors: [ + "Andrew Jeffery " + "Daniel Quernheim " + "Michael Bacarella " + "Sven Anderson " + "Tim McGilchrist " + "Wojtek Czekalski " + "dimitris.mostrous " +] +license: "BSD-3-Clause" +homepage: "https://github.com/dialohq/ocaml-grpc" +doc: "https://dialohq.github.io/ocaml-grpc" +bug-reports: "https://github.com/dialohq/ocaml-grpc/issues" +depends: [ + "dune" {>= "3.7"} + "grpc" {= version} + "ocaml-protoc-plugin" {>= "4.5"} + "odoc" {with-doc} +] +build: [ + ["dune" "subst"] {dev} + [ + "dune" + "build" + "-p" + name + "-j" + jobs + "@install" + "@runtest" {with-test} + "@doc" {with-doc} + ] +] +dev-repo: "git+https://github.com/dialohq/ocaml-grpc.git" diff --git a/lib/grpc-protoc-plugin/dune b/lib/grpc-protoc-plugin/dune new file mode 100644 index 0000000..900987e --- /dev/null +++ b/lib/grpc-protoc-plugin/dune @@ -0,0 +1,4 @@ +(library + (name grpc_protoc_plugin) + (public_name grpc-protoc-plugin) + (libraries grpc ocaml-protoc-plugin)) diff --git a/lib/grpc-protoc-plugin/grpc_protoc_plugin.ml b/lib/grpc-protoc-plugin/grpc_protoc_plugin.ml new file mode 100644 index 0000000..baea692 --- /dev/null +++ b/lib/grpc-protoc-plugin/grpc_protoc_plugin.ml @@ -0,0 +1,69 @@ +module type S = Ocaml_protoc_plugin.Service.Rpc + +let encode (type a) + (module M : Ocaml_protoc_plugin.Runtime.Runtime'.Service.Message + with type t = a) (a : a) = + a |> M.to_proto |> Ocaml_protoc_plugin.Runtime.Runtime'.Writer.contents + +let decode (type a) + (module M : Ocaml_protoc_plugin.Runtime.Runtime'.Service.Message + with type t = a) buffer = + buffer |> Ocaml_protoc_plugin.Runtime.Runtime'.Reader.create |> M.from_proto + |> function + | Ok r -> r + | Error e -> + failwith + (Printf.sprintf "Could not decode request: %s" + (Ocaml_protoc_plugin.Result.show_error e)) + +let service_spec (type request response) + (module R : S with type Request.t = request and type Response.t = response) + = + { + Grpc.Rpc.Service_spec.package = R.package_name |> Option.to_list; + service_name = R.service_name; + } + +module Client_rpc = struct + let make (type request response) + (module R : S with type Request.t = request and type Response.t = response) + ~request_mode ~response_mode = + { + Grpc.Rpc.Client_rpc.service_spec = service_spec (module R); + rpc_name = R.method_name; + encode_request = encode (module R.Request); + decode_response = decode (module R.Response); + request_mode; + response_mode; + } + + let unary rpc = make rpc ~request_mode:Unary ~response_mode:Unary + let client_streaming rpc = make rpc ~request_mode:Stream ~response_mode:Unary + let server_streaming rpc = make rpc ~request_mode:Unary ~response_mode:Stream + + let bidirectional_streaming rpc = + make rpc ~request_mode:Stream ~response_mode:Stream +end + +module Server_rpc = struct + let make (type request response) + (module R : S with type Request.t = request and type Response.t = response) + ~request_mode ~response_mode = + { + Grpc.Rpc.Server_rpc.service_spec = Some (service_spec (module R)); + rpc_name = R.method_name; + decode_request = decode (module R.Request); + encode_response = encode (module R.Response); + request_mode; + response_mode; + } + + let unary rpc = make rpc ~request_mode:Unary ~response_mode:Unary + let client_streaming rpc = make rpc ~request_mode:Stream ~response_mode:Unary + let server_streaming rpc = make rpc ~request_mode:Unary ~response_mode:Stream + + let bidirectional_streaming rpc = + make rpc ~request_mode:Stream ~response_mode:Stream +end + +let handlers handlers = Grpc.Rpc.Handlers.Handlers { handlers } diff --git a/lib/grpc-protoc-plugin/grpc_protoc_plugin.mli b/lib/grpc-protoc-plugin/grpc_protoc_plugin.mli new file mode 100644 index 0000000..b549936 --- /dev/null +++ b/lib/grpc-protoc-plugin/grpc_protoc_plugin.mli @@ -0,0 +1,92 @@ +(** A utility library for constructing gRPC specifications using + [Ocaml_protoc_plugin]. + + This module is designed to work alongside [Ocaml_protoc_plugin] to generate + gRPC stubs, as outlined in {!module:Grpc.Rpc}. It offers a collection of + helper functions that construct gRPC specifications from the code produced + by [Ocaml_protoc_plugin] based on the services defined in *.proto files. *) + +module type S = Ocaml_protoc_plugin.Service.Rpc +(** For each service delineated in *.proto files, [Ocaml_protoc_plugin] + generates a module that conforms to the type interface [S]. This module + serves as the entry point for this library to create the corresponding + gRPC specifications on the client and server sides. It is to be supplied + to the corresponding helper as a first class module parameter. *) + +(** {1 Client side} *) + +module Client_rpc : sig + val unary : + (module S with type Request.t = 'request and type Response.t = 'response) -> + ( 'request, + Grpc.Rpc.Value_mode.unary, + 'response, + Grpc.Rpc.Value_mode.unary ) + Grpc.Rpc.Client_rpc.t + + val client_streaming : + (module S with type Request.t = 'request and type Response.t = 'response) -> + ( 'request, + Grpc.Rpc.Value_mode.stream, + 'response, + Grpc.Rpc.Value_mode.unary ) + Grpc.Rpc.Client_rpc.t + + val server_streaming : + (module S with type Request.t = 'request and type Response.t = 'response) -> + ( 'request, + Grpc.Rpc.Value_mode.unary, + 'response, + Grpc.Rpc.Value_mode.stream ) + Grpc.Rpc.Client_rpc.t + + val bidirectional_streaming : + (module S with type Request.t = 'request and type Response.t = 'response) -> + ( 'request, + Grpc.Rpc.Value_mode.stream, + 'response, + Grpc.Rpc.Value_mode.stream ) + Grpc.Rpc.Client_rpc.t +end + +(** {1 Server side} *) + +module Server_rpc : sig + val unary : + (module S with type Request.t = 'request and type Response.t = 'response) -> + ( 'request, + Grpc.Rpc.Value_mode.unary, + 'response, + Grpc.Rpc.Value_mode.unary, + Grpc.Rpc.Service_spec.t ) + Grpc.Rpc.Server_rpc.t + + val client_streaming : + (module S with type Request.t = 'request and type Response.t = 'response) -> + ( 'request, + Grpc.Rpc.Value_mode.stream, + 'response, + Grpc.Rpc.Value_mode.unary, + Grpc.Rpc.Service_spec.t ) + Grpc.Rpc.Server_rpc.t + + val server_streaming : + (module S with type Request.t = 'request and type Response.t = 'response) -> + ( 'request, + Grpc.Rpc.Value_mode.unary, + 'response, + Grpc.Rpc.Value_mode.stream, + Grpc.Rpc.Service_spec.t ) + Grpc.Rpc.Server_rpc.t + + val bidirectional_streaming : + (module S with type Request.t = 'request and type Response.t = 'response) -> + ( 'request, + Grpc.Rpc.Value_mode.stream, + 'response, + Grpc.Rpc.Value_mode.stream, + Grpc.Rpc.Service_spec.t ) + Grpc.Rpc.Server_rpc.t +end + +val handlers : 'a list -> ('a, _) Grpc.Rpc.Handlers.t From 9abcb670c623b94821ec51a360a8e8daba2b4d8a Mon Sep 17 00:00:00 2001 From: Mathieu Barbin Date: Mon, 8 Jan 2024 15:34:04 +0100 Subject: [PATCH 3/4] Add helper library for using typed-rpc with ocaml-protoc --- dune-project | 15 +++++ grpc-protoc.opam | 42 ++++++++++++ lib/grpc-protoc/dune | 4 ++ lib/grpc-protoc/grpc_protoc.ml | 55 +++++++++++++++ lib/grpc-protoc/grpc_protoc.mli | 116 ++++++++++++++++++++++++++++++++ 5 files changed, 232 insertions(+) create mode 100644 grpc-protoc.opam create mode 100644 lib/grpc-protoc/dune create mode 100644 lib/grpc-protoc/grpc_protoc.ml create mode 100644 lib/grpc-protoc/grpc_protoc.mli diff --git a/dune-project b/dune-project index 8dd7965..1374a21 100644 --- a/dune-project +++ b/dune-project @@ -107,6 +107,21 @@ (ocaml-protoc-plugin (>= 4.5)))) +(package + (name grpc-protoc) + (synopsis "An implementation of gRPC using ocaml-protoc") + (description + "Functionality for building gRPC services and rpcs with `ocaml-protoc`") + (depends + (grpc + (= :version)) + (ocaml-protoc + (>= 3.0)) + (pbrt + (>= 3.0)) + (pbrt_services + (>= 3.0)))) + (package (name grpc-examples) (synopsis "Various gRPC examples") diff --git a/grpc-protoc.opam b/grpc-protoc.opam new file mode 100644 index 0000000..fde3dfd --- /dev/null +++ b/grpc-protoc.opam @@ -0,0 +1,42 @@ +# This file is generated by dune, edit dune-project instead +opam-version: "2.0" +synopsis: "An implementation of gRPC using ocaml-protoc" +description: + "Functionality for building gRPC services and rpcs with `ocaml-protoc`" +maintainer: ["Daniel Quernheim "] +authors: [ + "Andrew Jeffery " + "Daniel Quernheim " + "Michael Bacarella " + "Sven Anderson " + "Tim McGilchrist " + "Wojtek Czekalski " + "dimitris.mostrous " +] +license: "BSD-3-Clause" +homepage: "https://github.com/dialohq/ocaml-grpc" +doc: "https://dialohq.github.io/ocaml-grpc" +bug-reports: "https://github.com/dialohq/ocaml-grpc/issues" +depends: [ + "dune" {>= "3.7"} + "grpc" {= version} + "ocaml-protoc" {>= "3.0"} + "pbrt" {>= "3.0"} + "pbrt_services" {>= "3.0"} + "odoc" {with-doc} +] +build: [ + ["dune" "subst"] {dev} + [ + "dune" + "build" + "-p" + name + "-j" + jobs + "@install" + "@runtest" {with-test} + "@doc" {with-doc} + ] +] +dev-repo: "git+https://github.com/dialohq/ocaml-grpc.git" diff --git a/lib/grpc-protoc/dune b/lib/grpc-protoc/dune new file mode 100644 index 0000000..14a98b6 --- /dev/null +++ b/lib/grpc-protoc/dune @@ -0,0 +1,4 @@ +(library + (name grpc_protoc) + (public_name grpc-protoc) + (libraries grpc ocaml-protoc pbrt pbrt_services)) diff --git a/lib/grpc-protoc/grpc_protoc.ml b/lib/grpc-protoc/grpc_protoc.ml new file mode 100644 index 0000000..5ba88aa --- /dev/null +++ b/lib/grpc-protoc/grpc_protoc.ml @@ -0,0 +1,55 @@ +let encode (type a) (encode : a -> Pbrt.Encoder.t -> unit) (a : a) = + let encoder = Pbrt.Encoder.create () in + encode a encoder; + Pbrt.Encoder.to_string encoder + +let decode (type a) (decode : Pbrt.Decoder.t -> a) buffer = + let decoder = Pbrt.Decoder.of_string buffer in + decode decoder + +module Client_rpc = struct + let make (type request response) + (rpc : (request, _, response, _) Pbrt_services.Client.rpc) ~request_mode + ~response_mode = + { + Grpc.Rpc.Client_rpc.service_spec = + { package = rpc.package; service_name = rpc.service_name }; + rpc_name = rpc.rpc_name; + encode_request = encode rpc.encode_pb_req; + decode_response = decode rpc.decode_pb_res; + request_mode; + response_mode; + } + + let unary rpc = make rpc ~request_mode:Unary ~response_mode:Unary + let client_streaming rpc = make rpc ~request_mode:Stream ~response_mode:Unary + let server_streaming rpc = make rpc ~request_mode:Unary ~response_mode:Stream + + let bidirectional_streaming rpc = + make rpc ~request_mode:Stream ~response_mode:Stream +end + +module Server_rpc = struct + let make (type request response) + (rpc : (request, _, response, _) Pbrt_services.Server.rpc) ~request_mode + ~response_mode = + { + Grpc.Rpc.Server_rpc.service_spec = None; + rpc_name = rpc.name; + decode_request = decode rpc.decode_pb_req; + encode_response = encode rpc.encode_pb_res; + request_mode; + response_mode; + } + + let unary rpc = make rpc ~request_mode:Unary ~response_mode:Unary + let client_streaming rpc = make rpc ~request_mode:Stream ~response_mode:Unary + let server_streaming rpc = make rpc ~request_mode:Unary ~response_mode:Stream + + let bidirectional_streaming rpc = + make rpc ~request_mode:Stream ~response_mode:Stream +end + +let handlers { Pbrt_services.Server.package; service_name; handlers } = + Grpc.Rpc.Handlers.With_service_spec + { service_spec = { package; service_name }; handlers } diff --git a/lib/grpc-protoc/grpc_protoc.mli b/lib/grpc-protoc/grpc_protoc.mli new file mode 100644 index 0000000..8486af6 --- /dev/null +++ b/lib/grpc-protoc/grpc_protoc.mli @@ -0,0 +1,116 @@ +(** A utility library for constructing gRPC specifications using [Ocaml_protoc]. + + This module is designed to work alongside [Ocaml_protoc] to generate gRPC + stubs, as outlined in {!module:Grpc.Rpc}. It offers a collection of helper + functions that construct gRPC specifications from the code produced by + [Ocaml_protoc] based on the services defined in *.proto files. *) + +(** {1 Client side} *) + +module Client_rpc : sig + val unary : + ( 'request, + Pbrt_services.Value_mode.unary, + 'response, + Pbrt_services.Value_mode.unary ) + Pbrt_services.Client.rpc -> + ( 'request, + Grpc.Rpc.Value_mode.unary, + 'response, + Grpc.Rpc.Value_mode.unary ) + Grpc.Rpc.Client_rpc.t + + val client_streaming : + ( 'request, + Pbrt_services.Value_mode.stream, + 'response, + Pbrt_services.Value_mode.unary ) + Pbrt_services.Client.rpc -> + ( 'request, + Grpc.Rpc.Value_mode.stream, + 'response, + Grpc.Rpc.Value_mode.unary ) + Grpc.Rpc.Client_rpc.t + + val server_streaming : + ( 'request, + Pbrt_services.Value_mode.unary, + 'response, + Pbrt_services.Value_mode.stream ) + Pbrt_services.Client.rpc -> + ( 'request, + Grpc.Rpc.Value_mode.unary, + 'response, + Grpc.Rpc.Value_mode.stream ) + Grpc.Rpc.Client_rpc.t + + val bidirectional_streaming : + ( 'request, + Pbrt_services.Value_mode.stream, + 'response, + Pbrt_services.Value_mode.stream ) + Pbrt_services.Client.rpc -> + ( 'request, + Grpc.Rpc.Value_mode.stream, + 'response, + Grpc.Rpc.Value_mode.stream ) + Grpc.Rpc.Client_rpc.t +end + +(** {1 Server side} *) + +module Server_rpc : sig + val unary : + ( 'request, + Pbrt_services.Value_mode.unary, + 'response, + Pbrt_services.Value_mode.unary ) + Pbrt_services.Server.rpc -> + ( 'request, + Grpc.Rpc.Value_mode.unary, + 'response, + Grpc.Rpc.Value_mode.unary, + unit ) + Grpc.Rpc.Server_rpc.t + + val client_streaming : + ( 'request, + Pbrt_services.Value_mode.stream, + 'response, + Pbrt_services.Value_mode.unary ) + Pbrt_services.Server.rpc -> + ( 'request, + Grpc.Rpc.Value_mode.stream, + 'response, + Grpc.Rpc.Value_mode.unary, + unit ) + Grpc.Rpc.Server_rpc.t + + val server_streaming : + ( 'request, + Pbrt_services.Value_mode.unary, + 'response, + Pbrt_services.Value_mode.stream ) + Pbrt_services.Server.rpc -> + ( 'request, + Grpc.Rpc.Value_mode.unary, + 'response, + Grpc.Rpc.Value_mode.stream, + unit ) + Grpc.Rpc.Server_rpc.t + + val bidirectional_streaming : + ( 'request, + Pbrt_services.Value_mode.stream, + 'response, + Pbrt_services.Value_mode.stream ) + Pbrt_services.Server.rpc -> + ( 'request, + Grpc.Rpc.Value_mode.stream, + 'response, + Grpc.Rpc.Value_mode.stream, + unit ) + Grpc.Rpc.Server_rpc.t +end + +val handlers : 'a Pbrt_services.Server.t -> (_, 'a) Grpc.Rpc.Handlers.t From 8e47719286ffed230c71ba5ea7fb5e927296d1bb Mon Sep 17 00:00:00 2001 From: Mathieu Barbin Date: Wed, 10 Jan 2024 12:46:59 +0100 Subject: [PATCH 4/4] Add helpers for advanced users --- lib/grpc-protoc-plugin/grpc_protoc_plugin.mli | 10 ++++++++++ lib/grpc-protoc/grpc_protoc.ml | 19 +++++++++++++++---- lib/grpc-protoc/grpc_protoc.mli | 12 ++++++++++++ 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/lib/grpc-protoc-plugin/grpc_protoc_plugin.mli b/lib/grpc-protoc-plugin/grpc_protoc_plugin.mli index b549936..7801bab 100644 --- a/lib/grpc-protoc-plugin/grpc_protoc_plugin.mli +++ b/lib/grpc-protoc-plugin/grpc_protoc_plugin.mli @@ -90,3 +90,13 @@ module Server_rpc : sig end val handlers : 'a list -> ('a, _) Grpc.Rpc.Handlers.t + +(** {1 Advanced users API} + + The following functions are meant for advanced users only. *) + +val service_spec : + (module S with type Request.t = 'request and type Response.t = 'response) -> + Grpc.Rpc.Service_spec.t +(** This is the function used by this module to extract and build a gRPC service + spec from the information generated by [ocaml_protoc_plugin]. *) diff --git a/lib/grpc-protoc/grpc_protoc.ml b/lib/grpc-protoc/grpc_protoc.ml index 5ba88aa..3ef7076 100644 --- a/lib/grpc-protoc/grpc_protoc.ml +++ b/lib/grpc-protoc/grpc_protoc.ml @@ -7,13 +7,18 @@ let decode (type a) (decode : Pbrt.Decoder.t -> a) buffer = let decoder = Pbrt.Decoder.of_string buffer in decode decoder +let client_service_spec (rpc : _ Pbrt_services.Client.rpc) = + { + Grpc.Rpc.Service_spec.package = rpc.package; + service_name = rpc.service_name; + } + module Client_rpc = struct let make (type request response) (rpc : (request, _, response, _) Pbrt_services.Client.rpc) ~request_mode ~response_mode = { - Grpc.Rpc.Client_rpc.service_spec = - { package = rpc.package; service_name = rpc.service_name }; + Grpc.Rpc.Client_rpc.service_spec = client_service_spec rpc; rpc_name = rpc.rpc_name; encode_request = encode rpc.encode_pb_req; decode_response = decode rpc.decode_pb_res; @@ -50,6 +55,12 @@ module Server_rpc = struct make rpc ~request_mode:Stream ~response_mode:Stream end -let handlers { Pbrt_services.Server.package; service_name; handlers } = +let server_service_spec + { Pbrt_services.Server.package; service_name; handlers = _ } = + { Grpc.Rpc.Service_spec.package; service_name } + +let handlers + ({ Pbrt_services.Server.package = _; service_name = _; handlers } as server) + = Grpc.Rpc.Handlers.With_service_spec - { service_spec = { package; service_name }; handlers } + { service_spec = server_service_spec server; handlers } diff --git a/lib/grpc-protoc/grpc_protoc.mli b/lib/grpc-protoc/grpc_protoc.mli index 8486af6..9c0c03d 100644 --- a/lib/grpc-protoc/grpc_protoc.mli +++ b/lib/grpc-protoc/grpc_protoc.mli @@ -114,3 +114,15 @@ module Server_rpc : sig end val handlers : 'a Pbrt_services.Server.t -> (_, 'a) Grpc.Rpc.Handlers.t + +(** {1 Advanced users API} + + The following functions are meant for advanced users only. *) + +val client_service_spec : _ Pbrt_services.Client.rpc -> Grpc.Rpc.Service_spec.t +(** This is the function used by this module to extract and build a gRPC service + spec from the information generated by [ocaml_protoc] on the client side. *) + +val server_service_spec : _ Pbrt_services.Server.t -> Grpc.Rpc.Service_spec.t +(** This is the function used by this module to extract and build a gRPC service + spec from the information generated by [ocaml_protoc] on the server side. *)