dubbo-rs-codegen 0.1.0

protoc code generation plugin for Apache Dubbo Rust
Documentation

dubbo-rs-codegen

crates.io docs.rs Apache-2.0

protoc code generation plugin for dubbo-rs.

Installation

Add this to your Cargo.toml:

[dependencies]
dubbo-rs-codegen = "0.1"

Or use cargo add:

cargo add dubbo-rs-codegen

Integrates with tonic-prost-build to compile .proto files, generating standard proto types + tonic service stubs to OUT_DIR. Then produces Dubbo-specific integration wrappers (service registration helpers, channel-based and invoker-based clients) as GeneratedCode.

Key Types

Type Description
CodeGenerator Main entry point — takes GeneratorConfig, runs code generation
GeneratorConfig Configuration: proto_paths, output_dir, enable_client, enable_server, client_mode
GeneratorConfigBuilder Builder for GeneratorConfig — validates proto_paths is non-empty
GeneratedCode Output container — files: HashMap<String, String>, with write_to_dir
ClientMode Client generation mode: Channel, Invoker, or Both (default)

Client Modes

Mode Description
Channel Generates a channel-based client wrapping tonic's typed GreeterClient<Channel>
Invoker Generates an invoker-based client using Box<dyn Invoker> with prost encoding
Both (default) Generates both client types

Channel Client

Wraps the tonic-generated typed client. Provides connect(), from_channel(), and from_dubbo_client() constructors. Supports all 4 RPC types (unary, server-streaming, client-streaming, bidi-streaming).

pub struct GreeterChannelClient {
    inner: proto::greeter_client::GreeterClient<tonic::transport::Channel>,
}

impl GreeterChannelClient {
    pub async fn connect<D>(dst: D) -> Result<Self, tonic::transport::Error> { ... }
    pub fn from_channel(channel: tonic::transport::Channel) -> Self { ... }
    pub fn from_dubbo_client(client: &dubbo_rs_client::Client) -> Option<Self> { ... }
}

Invoker Client

Uses Box<dyn dubbo_rs_protocol::Invoker> with prost::Message encoding/decoding. Unary methods are fully supported. Streaming methods generate a comment indicating they are not supported via Invoker and the user should use the Channel client instead.

pub struct GreeterInvokerClient {
    invoker: Box<dyn dubbo_rs_protocol::Invoker>,
}

impl GreeterInvokerClient {
    pub fn new(invoker: Box<dyn dubbo_rs_protocol::Invoker>) -> Self { ... }
    pub async fn say_hello(&self, request: proto::HelloRequest) -> anyhow::Result<proto::HelloReply> {
        // Uses prost::Message::encode_to_vec / decode with path "/greeter.Greeter/SayHello"
    }
}

Usage

use dubbo_rs_codegen::{CodeGenerator, GeneratorConfigBuilder, ClientMode};

let config = GeneratorConfigBuilder::new()
    .proto_path("proto/hello.proto")
    .proto_path("proto/common.proto")
    .output_dir("src/gen")
    .enable_client(true)
    .enable_server(true)
    .client_mode(ClientMode::Both)
    .build()?;

let generator = CodeGenerator::new(config);
let generated = generator.generate()?;

// Write generated files to disk
generated.write_to_dir(std::path::Path::new("src/gen"))?;

Generated Output

For a proto with package greeter and service Greeter, generates greeter_dubbo.rs:

//! Dubbo integration for `greeter` package.
//! Generated by dubbo-codegen. DO NOT EDIT.

/// Proto types and tonic stubs.
pub mod proto {
    tonic::include_proto!("greeter");
}

// === Service Registration ===

/// Register a `Greeter` service with a Dubbo server.
pub fn register_greeter_service(
    server: dubbo_rs_server::Server,
    svc: impl proto::greeter_server::Greeter + 'static,
) -> dubbo_rs_server::Server {
    server.register_service(|builder| {
        builder.add_service(proto::greeter_server::GreeterServer::new(svc))
    })
}

// === Channel Client ===

pub struct GreeterChannelClient {
    inner: proto::greeter_client::GreeterClient<tonic::transport::Channel>,
}

impl GreeterChannelClient {
    pub async fn connect<D>(dst: D) -> Result<Self, tonic::transport::Error> { ... }
    pub fn from_channel(channel: tonic::transport::Channel) -> Self { ... }
    pub fn from_dubbo_client(client: &dubbo_rs_client::Client) -> Option<Self> { ... }
}

impl GreeterChannelClient {
    pub async fn say_hello(&mut self, request: impl Into<proto::HelloRequest>)
        -> Result<tonic::Response<proto::HelloReply>, tonic::Status> { ... }
}

// === Invoker Client ===

pub struct GreeterInvokerClient {
    invoker: Box<dyn dubbo_rs_protocol::Invoker>,
}

impl GreeterInvokerClient {
    pub fn new(invoker: Box<dyn dubbo_rs_protocol::Invoker>) -> Self { ... }
}

impl GreeterInvokerClient {
    pub async fn say_hello(&self, request: proto::HelloRequest)
        -> anyhow::Result<proto::HelloReply> { ... }
}

Streaming Support

All 4 RPC types are supported in generated code:

RPC Type Channel Client Invoker Client
Unary ✅ Forwarded to tonic ✅ prost encode/decode
Server Streaming tonic::codec::Streaming<T> ❌ Use Channel client
Client Streaming IntoStreamingRequest ❌ Use Channel client
Bidi Streaming ✅ Both streaming ❌ Use Channel client

Streaming Proto Example

For a streaming proto like:

service TelephoneExchange {
  rpc Dial(DialRequest) returns (stream DialProgress);
}

The generated channel client method returns:

pub async fn dial(&mut self, request: impl Into<proto::DialRequest>)
    -> Result<tonic::Response<tonic::codec::Streaming<proto::DialProgress>>, tonic::Status>

License

Apache-2.0