# goctl rpc — RPC Code Generation

English | [中文](README-cn.md) | [한국어](README-ko.md)

goctl rpc is the RPC service code generation module of the `goctl` scaffold. It generates a complete zRPC service from `.proto` files. You only need to write the proto definition and business logic — all boilerplate code is generated automatically.

## Features

- **protoc compatible**: Fully compatible with protoc, all protoc arguments are passed through
- **External proto imports**: Cross-directory and cross-package proto imports with automatic transitive dependency resolution
- **Multiple services**: Define multiple services in a single proto file, auto-grouped by service name
- **Streaming support**: Server streaming, client streaming, and bidirectional streaming
- **Google well-known types**: Automatic recognition of supported `google.protobuf.*` types with correct Go imports
- **Client generation**: Auto-generated RPC client wrapper code

## Prerequisites

```bash
# Install protoc plugins
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
```

## Quick Start

### Method 1: Create a Service Instantly

```bash
goctl rpc new greeter
```

Generates a complete project structure:

```
greeter/
├── etc/
│   └── greeter.yaml
├── greeter/
│   ├── greeter.pb.go
│   └── greeter_grpc.pb.go
├── greeter.go
├── greeter.proto
├── greeterclient/
│   └── greeter.go
└── internal/
    ├── config/
    │   └── config.go
    ├── logic/
    │   └── pinglogic.go
    ├── server/
    │   └── greeterserver.go
    └── svc/
        └── servicecontext.go
```

### Method 2: Generate from a Proto File

1. Generate a proto template:

```bash
goctl rpc template -o=user.proto
```

2. Initialize the output directory and generate service code:

```bash
mkdir -p output && cd output && go mod init example.com/demo && cd ..
goctl rpc protoc user.proto \
  --go_out=output --go-grpc_out=output --zrpc_out=output \
  --go_opt=module=example.com/demo --go-grpc_opt=module=example.com/demo \
  --module=example.com/demo -I .
```

---

## Command Reference

### `goctl rpc protoc`

Generate zRPC service code from a `.proto` file.

```bash
goctl rpc protoc <proto_file> [flags]
```

**Examples:**

```bash
# Basic usage
goctl rpc protoc user.proto \
  --go_out=output --go-grpc_out=output --zrpc_out=output \
  --go_opt=module=example.com/demo --go-grpc_opt=module=example.com/demo \
  --module=example.com/demo -I .

# Multiple services mode
goctl rpc protoc multi.proto \
  --go_out=output --go-grpc_out=output --zrpc_out=output \
  --go_opt=module=example.com/demo --go-grpc_opt=module=example.com/demo \
  --module=example.com/demo -I . -m

# Import external protos
goctl rpc protoc service.proto \
  --go_out=output --go-grpc_out=output --zrpc_out=output \
  --go_opt=module=example.com/demo --go-grpc_opt=module=example.com/demo \
  --module=example.com/demo -I . -I ./shared_protos

# Use Google well-known types
goctl rpc protoc service.proto \
  --go_out=output --go-grpc_out=output --zrpc_out=output \
  --go_opt=module=example.com/demo --go-grpc_opt=module=example.com/demo \
  --module=example.com/demo -I .
```

**Flags:**

| Flag | Short | Type | Default | Description |
|------|-------|------|---------|-------------|
| `--zrpc_out` | | string | **required** | Output directory for zRPC service code |
| `--go_out` | | string | **required** | Output directory for protoc Go code |
| `--go-grpc_out` | | string | **required** | Output directory for protoc gRPC code |
| `--go_opt` | | string | | Options for protoc-gen-go (e.g., `module=example.com/demo`) |
| `--go-grpc_opt` | | string | | Options for protoc-gen-go-grpc (e.g., `module=example.com/demo`) |
| `--proto_path` | `-I` | string[] | | Proto import search directories (repeatable) |
| `--multiple` | `-m` | bool | `false` | Multiple services mode |
| `--client` | `-c` | bool | `true` | Generate RPC client code |
| `--style` | | string | `gozero` | File naming style |
| `--module` | | string | | Custom Go module name |
| `--name-from-filename` | | bool | `false` | Use filename instead of package name for service naming |
| `--verbose` | `-v` | bool | `false` | Enable verbose logging |
| `--home` | | string | | goctl template directory |
| `--remote` | | string | | Remote template Git repository URL |
| `--branch` | | string | | Remote template branch |

### `goctl rpc new`

Quickly create a complete RPC service project.

```bash
goctl rpc new <service_name> [flags]
```

**Flags:**

| Flag | Short | Type | Default | Description |
|------|-------|------|---------|-------------|
| `--style` | | string | `gozero` | File naming style |
| `--client` | `-c` | bool | `true` | Generate RPC client code |
| `--module` | | string | | Custom Go module name |
| `--verbose` | `-v` | bool | `false` | Enable verbose logging |
| `--idea` | | bool | `false` | Generate IDE project marker |
| `--name-from-filename` | | bool | `false` | Use filename instead of package name for service naming |
| `--home` | | string | | goctl template directory |
| `--remote` | | string | | Remote template Git repository URL |
| `--branch` | | string | | Remote template branch |

### `goctl rpc template`

Generate a proto file template.

```bash
goctl rpc template -o=<output_file> [flags]
```

**Flags:**

| Flag | Type | Description |
|------|------|-------------|
| `-o` | string | Output file path (required) |
| `--home` | string | goctl template directory |
| `--remote` | string | Remote template Git repository URL |
| `--branch` | string | Remote template branch |

---

## Feature Details

### Multiple Services Mode (`--multiple`)

When a proto file contains multiple `service` definitions, the `--multiple` flag is required.

```protobuf
service SearchService {
  rpc Search(SearchReq) returns (SearchReply);
}

service NotifyService {
  rpc Notify(NotifyReq) returns (NotifyReply);
}
```

**Directory differences with `--multiple`:**

| Feature | Default mode | `--multiple` mode |
|---------|-------------|-------------------|
| Services per proto | Exactly 1 | 1 or more |
| Client directory | Named after service | Fixed `client/` directory |
| Code organization | Flat structure | Grouped by service name |

**`--multiple=false` (default) directory structure:**

```
output/
├── greeterclient/
│   └── greeter.go
├── internal/
│   ├── logic/
│   │   └── sayhellologic.go
│   └── server/
│       └── greeterserver.go
└── ...
```

**`--multiple=true` directory structure:**

```
output/
├── client/
│   ├── searchservice/
│   │   └── searchservice.go
│   └── notifyservice/
│       └── notifyservice.go
├── internal/
│   ├── logic/
│   │   ├── searchservice/
│   │   │   └── searchlogic.go
│   │   └── notifyservice/
│   │       └── notifylogic.go
│   └── server/
│       ├── searchservice/
│       │   └── searchserviceserver.go
│       └── notifyservice/
│           └── notifyserviceserver.go
└── ...
```

### External Proto Imports (`--proto_path`)

Use `-I` / `--proto_path` to specify additional proto search directories. Supported scenarios:

- **Same-directory import**: `import "types.proto";`
- **Subdirectory import**: `import "common/types.proto";`
- **External directory import**: Proto files outside the project
- **Transitive imports**: A imports B, B imports C — goctl resolves recursively
- **Cross-package imports**: Different `go_package` values generate correct Go imports automatically

```bash
# Search multiple directories for proto files
goctl rpc protoc service.proto \
  --go_out=output --go-grpc_out=output --zrpc_out=output \
  --go_opt=module=example.com/demo --go-grpc_opt=module=example.com/demo \
  --module=example.com/demo \
  -I . -I ./shared_protos -I /path/to/external_protos
```

### Service Naming

By default, the service name is derived from the proto **package name** (e.g., `package user;` → service name `user`). This allows multiple proto files to share the same package:

```
protos/
├── user_base.proto      # package user;
├── user_auth.proto      # package user;
└── user_profile.proto   # package user;
```

All three files generate into a single `user` service.

To use the proto filename for naming (legacy behavior), add the `--name-from-filename` flag.

### Streaming RPC

All three gRPC streaming patterns are supported:

```protobuf
service StreamService {
  rpc ServerStream(Req) returns (stream Reply);       // Server streaming
  rpc ClientStream(stream Req) returns (Reply);       // Client streaming
  rpc BidiStream(stream Req) returns (stream Reply);  // Bidirectional streaming
}
```

### Google Well-Known Types

goctl automatically recognizes and handles Google protobuf well-known types:

| Proto Type | Go Type |
|-----------|---------|
| `google.protobuf.Empty` | `emptypb.Empty` |
| `google.protobuf.Timestamp` | `timestamppb.Timestamp` |
| `google.protobuf.Duration` | `durationpb.Duration` |
| `google.protobuf.Any` | `anypb.Any` |
| `google.protobuf.Struct` | `structpb.Struct` |
| `google.protobuf.FieldMask` | `fieldmaskpb.FieldMask` |
| `google.protobuf.*Value` | `wrapperspb.*Value` |

These types can be used directly as RPC parameter types — goctl generates the correct imports automatically.

---

## Examples

See the [example/](example/) directory for 10 complete examples covering all generation scenarios.

| # | Example | Scenario |
|---|---------|----------|
| 01 | [Basic service](example/01-basic/) | Single service, no imports |
| 02 | [Sibling import](example/02-import-sibling/) | Import from same directory |
| 03 | [Subdirectory import](example/03-import-subdir/) | Import from subdirectory |
| 04 | [Transitive import](example/04-transitive-import/) | A → B → C dependency chain |
| 05 | [Multiple services](example/05-multiple-services/) | `--multiple` mode |
| 06 | [Well-known types](example/06-wellknown-types/) | Timestamp etc. in messages |
| 07 | [External proto (same pkg)](example/07-external-proto-same-pkg/) | External proto, same go_package |
| 08 | [External proto (diff pkg)](example/08-external-proto-diff-pkg/) | External proto, different go_package |
| 09 | [Google well-known types as params](example/09-google-types-as-rpc/) | Empty/Timestamp as RPC parameters |
| 10 | [Streaming](example/10-streaming/) | Server/client/bidirectional streaming |
