Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add typed rpc helpers for ocaml-protoc-plugin and ocaml-protoc #54

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions dune-project
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@
(>= 0.9.1))
(h2
(>= 0.9.0))
(odoc
(and
(>= 2.4.0)
:with-doc))
ppx_deriving
(uri
(>= 4.0.0))))
Expand Down Expand Up @@ -92,6 +96,32 @@
(>= 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-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")
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 <grpc@quernd.uber.space>"]
authors: [
"Andrew Jeffery <dev@jeffas.io>"
"Daniel Quernheim <quernd@users.noreply.github.com>"
"Michael Bacarella <m@bacarella.com>"
"Sven Anderson <sven@anderson.de>"
"Tim McGilchrist <timmcgil@gmail.com>"
"Wojtek Czekalski <me@wczekalski.com>"
"dimitris.mostrous <dimitris.mostrous@gmail.com>"
]
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"
42 changes: 42 additions & 0 deletions grpc-protoc.opam
Original file line number Diff line number Diff line change
@@ -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 <grpc@quernd.uber.space>"]
authors: [
"Andrew Jeffery <dev@jeffas.io>"
"Daniel Quernheim <quernd@users.noreply.github.com>"
"Michael Bacarella <m@bacarella.com>"
"Sven Anderson <sven@anderson.de>"
"Tim McGilchrist <timmcgil@gmail.com>"
"Wojtek Czekalski <me@wczekalski.com>"
"dimitris.mostrous <dimitris.mostrous@gmail.com>"
]
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"
2 changes: 1 addition & 1 deletion grpc.opam
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
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 }
102 changes: 102 additions & 0 deletions lib/grpc-protoc-plugin/grpc_protoc_plugin.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
(** 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

(** {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]. *)
4 changes: 4 additions & 0 deletions lib/grpc-protoc/dune
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
(library
(name grpc_protoc)
(public_name grpc-protoc)
(libraries grpc ocaml-protoc pbrt pbrt_services))
66 changes: 66 additions & 0 deletions lib/grpc-protoc/grpc_protoc.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
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

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 = client_service_spec rpc;
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 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 = server_service_spec server; handlers }
Loading
Loading