Skip to content

Commit

Permalink
Add helper library for using typed-rpc with ocaml-protoc-plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
mbarbin committed Jan 8, 2024
1 parent 1908d45 commit 8127f46
Show file tree
Hide file tree
Showing 5 changed files with 216 additions and 0 deletions.
11 changes: 11 additions & 0 deletions dune-project
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
40 changes: 40 additions & 0 deletions grpc-protoc-plugin.opam
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>"]
authors: [
"Andrew Jeffery <[email protected]>"
"Daniel Quernheim <[email protected]>"
"Michael Bacarella <[email protected]>"
"Sven Anderson <[email protected]>"
"Tim McGilchrist <[email protected]>"
"Wojtek Czekalski <[email protected]>"
"dimitris.mostrous <[email protected]>"
]
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"
4 changes: 4 additions & 0 deletions lib/grpc-protoc-plugin/dune
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
(library
(name grpc_protoc_plugin)
(public_name grpc-protoc-plugin)
(libraries grpc ocaml-protoc-plugin))
69 changes: 69 additions & 0 deletions lib/grpc-protoc-plugin/grpc_protoc_plugin.ml
Original file line number Diff line number Diff line change
@@ -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 }
92 changes: 92 additions & 0 deletions lib/grpc-protoc-plugin/grpc_protoc_plugin.mli
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 8127f46

Please sign in to comment.