dubbo-rs-codegen 0.1.0

protoc code generation plugin for Apache Dubbo Rust
Documentation
# dubbo-rs-codegen

[![crates.io](https://img.shields.io/crates/v/dubbo-rs-codegen)](https://crates.io/crates/dubbo-rs-codegen)
[![docs.rs](https://docs.rs/dubbo-rs-codegen/badge.svg)](https://docs.rs/dubbo-rs-codegen)
[![Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)

protoc code generation plugin for dubbo-rs.

## Installation

Add this to your `Cargo.toml`:

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

Or use `cargo add`:

```bash
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).

```rust
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.

```rust
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

```rust
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`:

```rust
//! 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:

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

The generated channel client method returns:

```rust
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