Skip to content
Merged
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
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ gRPC services are declared in `.proto` files. Use `gRPCClient.generate` to gener

gRPC code generation uses `protoc` and the `ProtoBuf.jl` package. To be able to generate gRPC client code, `ProtoBuf` package must be installed along with `gRPCClient`.

The `protoc` file must have service generation turned on for at least one of C++, python or Java, e.g. one of:

```
option cc_generic_services = true;
option py_generic_services = true;
option java_generic_services = true;
```

The Julia code generated can be improper if the `package` name declared in the proto specification has `.`. Set a suitable `package` name without `.`.

```julia
julia> using Pkg

Expand Down
38 changes: 33 additions & 5 deletions src/generate.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const package_regex = r"package\s(\S*)[\s]*;.*"
const service_regex = r"service\s(\S*)[\s]*{.*"
const services_option_regex = r"option\s[a-z]*_generic_services[\s]*=[\s]*true[\s]*;"

function write_header(io, package, client_module_name)
print(io, """module $(client_module_name)
Expand Down Expand Up @@ -88,6 +89,19 @@ function write_service_method(io, package, service, method)
""")
end

function has_services_enabled(proto::String)
enabled = false
for line in readlines(proto)
line = strip(line)
regexmatches = match(services_option_regex, line)
if (regexmatches !== nothing)
enabled = true
break
end
end
enabled
end

function detect_services(proto::String)
package = ""
services = String[]
Expand All @@ -110,6 +124,14 @@ function detect_services(proto::String)
package, services
end

function get_generated_method_table(s::String)
T = Main
for t in split(s, ".")
T = Base.eval(T, Symbol(t))
end
T
end

"""
generate(proto::String; outdir::String=pwd())

Expand All @@ -127,6 +149,11 @@ function generate(proto::String; outdir::String=pwd())

@info("Generating gRPC client", proto, outdir)

# ensure services are enabled in proto file
if !has_services_enabled(proto)
throw(ArgumentError("Service generation must be enabled in $proto, e.g. option py_generic_services = true;"))
end

# determine the package name and service name
package, services = detect_services(proto)
protodir = dirname(proto)
Expand All @@ -141,19 +168,20 @@ function generate(proto::String; outdir::String=pwd())
end

# include the generated code and detect service method names
generated_module = joinpath(outdir, "$(package).jl")
Main.eval(:(include($generated_module)))
generated_module = first(split(package, '.'; limit=2))
generated_module_file = joinpath(outdir, string(generated_module, ".jl"))
Main.eval(:(include($generated_module_file)))

# generate the gRPC client code
client_module_name = string(titlecase(package), "Clients")
client_module_name = string(titlecase(package; strict=false), "Clients")
open(joinpath(outdir, "$(client_module_name).jl"), "w") do grpcservice
write_header(grpcservice, package, client_module_name)
for service in services
methods = Base.eval(Base.eval(Main, Symbol(package)), Symbol(string("_", service, "_methods")))
methods = get_generated_method_table(string(package, "._", service, "_methods"))
write_service(grpcservice, package, service, methods)
end
write_trailer(grpcservice, client_module_name)
end

@info("Generated", outdir)
end
end