granc/granc-core/tests/echo_service_impl.rs
Víctor Martínez c9ef611e07
[feat] Generate markdown documentation for gRPC services (#46)
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`)
2026-02-06 13:16:19 +01:00

89 lines
2.8 KiB
Rust

use futures_util::Stream;
use futures_util::StreamExt;
use granc_test_support::echo_service::EchoService;
use granc_test_support::echo_service::pb::{EchoRequest, EchoResponse};
use std::pin::Pin;
use tokio::sync::mpsc;
use tokio_stream::wrappers::ReceiverStream;
use tonic::{Request, Response, Status, Streaming};
#[derive(Debug)]
pub struct EchoServiceImpl;
#[tonic::async_trait]
impl EchoService for EchoServiceImpl {
type BidirectionalEchoStream = Pin<Box<dyn Stream<Item = Result<EchoResponse, Status>> + Send>>;
type ServerStreamingEchoStream = ReceiverStream<Result<EchoResponse, Status>>;
async fn unary_echo(
&self,
request: Request<EchoRequest>,
) -> Result<Response<EchoResponse>, Status> {
Ok(Response::new(EchoResponse {
message: request.into_inner().message,
}))
}
async fn server_streaming_echo(
&self,
request: Request<EchoRequest>,
) -> Result<Response<Self::ServerStreamingEchoStream>, Status> {
let msg = request.into_inner().message;
let (tx, rx) = mpsc::channel(4);
tokio::spawn(async move {
for i in 0..3 {
let response = EchoResponse {
message: format!("{} - seq {}", msg, i),
};
tx.send(Ok(response)).await.ok();
}
});
Ok(Response::new(ReceiverStream::new(rx)))
}
async fn client_streaming_echo(
&self,
request: Request<Streaming<EchoRequest>>,
) -> Result<Response<EchoResponse>, Status> {
let mut stream = request.into_inner();
let mut full_msg = String::new();
while let Some(req) = stream.next().await {
let req = req?;
full_msg.push_str(&req.message);
}
Ok(Response::new(EchoResponse { message: full_msg }))
}
async fn bidirectional_echo(
&self,
request: Request<Streaming<EchoRequest>>,
) -> Result<Response<Self::BidirectionalEchoStream>, Status> {
let mut in_stream = request.into_inner();
let (tx, rx) = mpsc::channel(128);
tokio::spawn(async move {
while let Some(result) = in_stream.next().await {
match result {
Ok(req) => {
let resp = EchoResponse {
message: format!("echo: {}", req.message),
};
if tx.send(Ok(resp)).await.is_err() {
break;
}
}
Err(e) => {
let _ = tx.send(Err(e)).await;
break;
}
}
}
});
Ok(Response::new(Box::pin(ReceiverStream::new(rx))))
}
}