mirror of
https://codeberg.org/JasterV/granc.git
synced 2026-04-26 18:40:05 +00:00
This PR implements a new subcommand `doc` that generates markdown documentation for a given gRPC service! **Description** For the most part, the inner logic of this subcommand is the same as the `describe`, the only thing that changes is the way that the found descriptor is transformed to a final output. In this case, a `Packages` type has been implemented to transform a `ServiceDescriptor` into a map of `Package`s. Each package groups all the file descriptors with the same package name (or namespace). A `Package` contains all the necessary information for a file of documentation to be generated (All its contained services, messages and enum descriptors and its name). The output of this command is a folder with all the generated documentation, which contains a file per protobuf package. **Introduced the `granc-test-support` crate** * Renamed the `echo_service` crate as `granc-test-support`, providing both the definition of a protobuf service for integration testing and a function to compile protobuffer at runtime into a file descriptor (Potentially this could be used to let users pass a folder to a proto project in addition to the server reflection and the local file descriptor options. For example, the `call` command could compile a file descriptor on the fly from a folder containing a protobuffer project before making the call to the gRPC server. **Descriptor API Enhancements:** * Added `name`, `full_name`, and `package_name` methods to the `Descriptor` enum to simplify access to descriptor metadata. (`granc-core/src/client/types.rs`) **Dependency Management Improvements:** * Added grouping for gRPC-related dependencies in `dependabot.yml` for improved automated dependency updates. (`.github/dependabot.yml`)
290 lines
9.6 KiB
Markdown
290 lines
9.6 KiB
Markdown
# Granc 🦀
|
|
|
|
[](https://crates.io/crates/granc)
|
|
[](https://github.com/JasterV/granc/blob/main/LICENSE)
|
|
|
|
> ⚠️ **Status: Experimental**
|
|
>
|
|
> This project is a working prototype intended for testing and development purposes. APIs, command-line arguments, and internal logic are subject to breaking changes. Please use with caution.
|
|
|
|
**Granc** (gRPC + Cranc, Crab in Catalan) is a lightweight, dynamic gRPC CLI tool written in Rust.
|
|
|
|
It allows you to make gRPC calls to any server using simple JSON payloads, without needing to compile the specific Protobuf files into the client. By loading a `FileDescriptorSet` at runtime, granc acts as a bridge between human-readable JSON and binary Protobuf wire format.
|
|
|
|
It is heavily inspired by tools like `grpcurl` but built to leverage the safety and performance of the Rust ecosystem (Tonic + Prost).
|
|
|
|
## 🚀 Features
|
|
|
|
* **Dynamic Encoding/Decoding**: Transcodes JSON to Protobuf (and vice versa) on the fly using `prost-reflect`.
|
|
* **Metadata Support**: Easily attach custom headers (authorization, tracing) to your requests.
|
|
* **Fast Fail Validation**: Validates your JSON *before* hitting the network.
|
|
* **Smart Dispatch**: Automatically detects if a call is Unary, Server Streaming, Client Streaming, or Bidirectional based on the descriptor.
|
|
* **Server Reflection**: Can fetch schemas directly from the server, removing the need to pass a local file descriptor set file (`.bin` or `.pb`).
|
|
* **Introspection Tools**: Commands to list services and describe services, messages, and enums.
|
|
* **Local Introspection**: In addition to making network requests, Granc can also be used as a local introspection tool for file descriptor binary files. You can load a local `.bin` file to inspect services, messages, and enums without needing to fetch the schema from a server.
|
|
* **Documentation Generator**: Generate static, cross-linked Markdown documentation for your services and types directly from the schema. [See a real example](./examples/docs/index.md) generated from this repo's [example protos](./examples/proto/library).
|
|
* **Zero Compilation Dependencies**: Does not require generating Rust code for your protos. Just point to a descriptor file.
|
|
* **Tonic 0.14**: Built on the latest stable Rust gRPC stack.
|
|
|
|
## 📦 Installation
|
|
|
|
```bash
|
|
cargo install --locked granc
|
|
|
|
```
|
|
|
|
## 🛠️ Prerequisites
|
|
|
|
Granc needs to know the schema of the service you are calling. It can obtain this in two ways:
|
|
|
|
1. **Automatic Server Reflection**: If the server has [Server Reflection](https://github.com/grpc/grpc/blob/master/doc/server-reflection.md) enabled, Granc can download the schema automatically.
|
|
2. **Local Descriptor File**: You can provide a binary `FileDescriptorSet` (`.bin`) generated by `protoc`.
|
|
|
|
### Generating Descriptors (Optional)
|
|
|
|
If your server does not support reflection, you must generate a descriptor file:
|
|
|
|
```bash
|
|
# Generate descriptor.bin including all imports
|
|
protoc \
|
|
--include_imports \
|
|
--descriptor_set_out=descriptor.bin \
|
|
--proto_path=. \
|
|
my_service.proto
|
|
|
|
```
|
|
|
|
> **Note**: The `--include_imports` flag is crucial. It ensures that types defined in imported files (like `google/protobuf/timestamp.proto`) are available for reflection.
|
|
|
|
## 📖 Usage
|
|
|
|
**Syntax:**
|
|
|
|
```bash
|
|
granc <COMMAND> [ARGS]
|
|
|
|
```
|
|
|
|
### Commands
|
|
|
|
#### 1. `call` (Make Requests)
|
|
|
|
Performs a gRPC call using a JSON body.
|
|
|
|
```bash
|
|
granc call <ENDPOINT> --uri <URI> --body <JSON> [OPTIONS]
|
|
|
|
```
|
|
|
|
| Argument/Flag | Short | Description | Required |
|
|
| --- | --- | --- | --- |
|
|
| `<ENDPOINT>` | | Fully qualified method name (e.g., `my.package.Service/Method`). | **Yes** |
|
|
| `--uri` | `-u` | Server address (e.g., `http://[::1]:50051`). | **Yes** |
|
|
| `--body` | `-b` | The request body in JSON format. Object `{}` for unary, Array `[]` for streaming. | **Yes** |
|
|
| `--header` | `-H` | Custom header `key:value`. Can be used multiple times. | No |
|
|
| `--file-descriptor-set` | `-f` | Path to a local `.bin` descriptor file to use instead of reflection. | No |
|
|
|
|
**Example using Server Reflection:**
|
|
|
|
```bash
|
|
granc call helloworld.Greeter/SayHello --uri http://localhost:50051 --body '{"name": "Ferris"}'
|
|
|
|
```
|
|
|
|
```json
|
|
{
|
|
"message": "Hello Ferris"
|
|
}
|
|
|
|
```
|
|
|
|
**Example using a Local Descriptor File:**
|
|
|
|
```bash
|
|
granc call helloworld.Greeter/SayHello \
|
|
--uri http://localhost:50051 \
|
|
--file-descriptor-set ./descriptors.bin \
|
|
--body '{"name": "Ferris"}'
|
|
|
|
```
|
|
|
|
#### 2. `list` (Service Discovery)
|
|
|
|
Lists all services exposed by the server (via reflection) or contained in the provided descriptor file. You must provide **either** a URI or a file descriptor set.
|
|
|
|
```bash
|
|
granc list [OPTIONS]
|
|
|
|
```
|
|
|
|
| Flag | Short | Description |
|
|
| --- | --- | --- |
|
|
| `--uri` | `-u` | Use Server Reflection to list available services. |
|
|
| `--file-descriptor-set` | `-f` | Use a local file to list contained services (offline). |
|
|
|
|
**Listing services via Reflection:**
|
|
|
|
```bash
|
|
granc list --uri http://localhost:50051
|
|
|
|
```
|
|
|
|
```
|
|
Available Services:
|
|
- grpc.reflection.v1.ServerReflection
|
|
- helloworld.Greeter
|
|
|
|
```
|
|
|
|
**Listing services from a file (Offline):**
|
|
|
|
```bash
|
|
granc list --file-descriptor-set ./descriptors.bin
|
|
|
|
```
|
|
|
|
#### 3. `describe` (Introspection)
|
|
|
|
Inspects a specific symbol (Service, Message, or Enum) and prints its Protobuf definition in a colored, human-readable format. You must provide **either** a URI or a file descriptor set.
|
|
|
|
```bash
|
|
granc describe <SYMBOL> [OPTIONS]
|
|
|
|
```
|
|
|
|
| Argument/Flag | Short | Description |
|
|
| --- | --- | --- |
|
|
| `<SYMBOL>` | | Fully qualified name of the Service, Message, or Enum. |
|
|
| `--uri` | `-u` | Use Server Reflection to resolve the symbol. |
|
|
| `--file-descriptor-set` | `-f` | Use a local file to resolve the symbol (offline). |
|
|
|
|
**Describing a Service via Reflection:**
|
|
|
|
```bash
|
|
granc describe helloworld.Greeter --uri http://localhost:50051
|
|
|
|
```
|
|
|
|
```proto
|
|
service Greeter {
|
|
rpc SayHello(helloworld.HelloRequest) returns (helloworld.HelloReply);
|
|
rpc StreamHello(stream helloworld.HelloRequest) returns (stream helloworld.HelloReply);
|
|
}
|
|
|
|
```
|
|
|
|
**Describing a Message using a Local File:**
|
|
|
|
```bash
|
|
granc describe helloworld.HelloRequest --file-descriptor-set ./descriptors.bin
|
|
|
|
```
|
|
|
|
```proto
|
|
message HelloRequest {
|
|
string name = 1;
|
|
int32 age = 2;
|
|
repeated string tags = 3;
|
|
}
|
|
|
|
```
|
|
|
|
**Describing an Enum:**
|
|
|
|
```bash
|
|
granc describe my.package.Status --uri http://localhost:50051
|
|
|
|
```
|
|
|
|
```proto
|
|
enum Status {
|
|
UNKNOWN = 0;
|
|
ACTIVE = 1;
|
|
INACTIVE = 2;
|
|
}
|
|
|
|
```
|
|
|
|
#### 4. `doc` (Documentation Generator)
|
|
|
|
Generates static Markdown documentation for a specific service and its dependencies. This is useful for creating browseable documentation for your gRPC APIs.
|
|
|
|
```bash
|
|
granc doc <SYMBOL> --output <DIR> [OPTIONS]
|
|
```
|
|
|
|
| Argument/Flag | Short | Description |
|
|
| --- | --- | --- |
|
|
| `<SYMBOL>` | | Fully qualified name of the Service (e.g., `library.LibraryService`). |
|
|
| `--output` | `-o` | Directory where the markdown files will be generated. |
|
|
| `--uri` | `-u` | Use Server Reflection to resolve the schema. |
|
|
| `--file-descriptor-set` | `-f` | Use a local file to resolve the schema (offline). |
|
|
|
|
**Generating docs via Reflection:**
|
|
|
|
```bash
|
|
granc doc helloworld.Greeter --uri http://localhost:50051 --output ./docs
|
|
```
|
|
|
|
**Generating docs from a file:**
|
|
|
|
```bash
|
|
granc doc library.LibraryService --file-descriptor-set examples/library.bin --output ./docs
|
|
```
|
|
|
|
Check out the full [generated documentation example](./examples/docs/index.md) included in this repository.
|
|
These documents were generated directly from the [library example protos](./examples/proto/library) using the command above.
|
|
|
|
## 🔮 Roadmap
|
|
|
|
* **Interactive Mode**: A REPL for streaming requests interactively.
|
|
* **Pretty Printing JSON**: Enhanced colored output for JSON responses.
|
|
* **TLS Support**: Configurable root certificates and client identity.
|
|
|
|
## 🧩 Using as a Library
|
|
|
|
The core logic of Granc is decoupled into a separate library crate, **`granc-core`**.
|
|
|
|
If you want to build your own tools using the dynamic gRPC engine (e.g., for custom integration testing, proxies, or automation tools), you can depend on `granc-core` directly.
|
|
|
|
* **Documentation & Usage**: See the **[`granc-core` README](./granc-core/README.md)** for examples on how to use the `GrancClient` programmatically.
|
|
* **Crate**: [`granc-core`](https://crates.io/crates/granc_core)
|
|
|
|
## ⚠️ Common Errors
|
|
|
|
**1. `Service 'x' not found**`
|
|
|
|
* **Cause:** The service name in the command does not match the package defined in your proto file.
|
|
* **Fix:** Check your `.proto` file. If it has `package my.app;` and `service API {}`, the full name is `my.app.API`.
|
|
|
|
**2. `Method 'y' not found in service 'x'**`
|
|
|
|
* **Cause:** Typo in the method name or the method doesn't exist.
|
|
* **Fix:** Ensure case sensitivity matches (e.g., `GetUser` vs `getUser`).
|
|
|
|
**3. `h2 protocol error**`
|
|
|
|
* **Cause:** This often occurs when the JSON payload fails to encode *after* the connection has already been established, or the server rejected the stream structure.
|
|
* **Fix:** Double-check your JSON payload against the Protobuf schema.
|
|
|
|
## 🤝 Contributing
|
|
|
|
Contributions are welcome! Please run the Makefile checks before submitting a PR:
|
|
|
|
```bash
|
|
cargo make ci # Checks formatting, lints, and runs tests
|
|
|
|
```
|
|
|
|
## 📄 License
|
|
|
|
Licensed under either of:
|
|
|
|
* Apache License, Version 2.0 ([LICENSE-APACHE](http://www.apache.org/licenses/LICENSE-2.0))
|
|
* MIT license ([LICENSE-MIT](http://opensource.org/licenses/MIT))
|
|
|
|
at your option.
|
|
|
|
### Contribution
|
|
|
|
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
|