refactor: decouple ReflectionClient to possibly publish in a separate crate

This commit is contained in:
JasterV 2026-01-21 15:49:56 +01:00
parent 5f6ffaa3ab
commit 27674a03ab
3 changed files with 44 additions and 25 deletions

View file

@ -72,10 +72,9 @@ pub async fn run(input: Input) -> Result<Output, CoreError> {
Some(path) => DescriptorRegistry::from_file(path)?,
// If no proto-set file is passed, we'll try to reach the server reflection service
None => {
let mut service = ReflectionClient::connect(input.url.clone()).await?;
service
.resolve_service_descriptor_registry(&input.service)
.await?
let mut client = ReflectionClient::connect(input.url.clone()).await?;
let fd_set = client.file_descriptor_set_by_symbol(&input.service).await?;
DescriptorRegistry::from_file_descriptor_set(fd_set)?
}
};

View file

@ -19,7 +19,6 @@ use super::generated::reflection_v1::{
server_reflection_response::MessageResponse,
};
use crate::core::BoxError;
use crate::core::reflection::DescriptorRegistry;
use http_body::Body as HttpBody;
use prost::Message;
use prost_types::{FileDescriptorProto, FileDescriptorSet};
@ -64,9 +63,6 @@ pub enum ReflectionResolveError {
#[error("Failed to decode FileDescriptorProto: {0}")]
DecodeError(#[from] prost::DecodeError),
#[error("Failed to build DescriptorRegistry: {0}")]
RegistryError(#[from] crate::core::reflection::registry::DescriptorError),
}
/// A generic client for the gRPC Server Reflection Protocol.
@ -99,10 +95,21 @@ where
T::ResponseBody: HttpBody<Data = tonic::codegen::Bytes> + Send + 'static,
<T::ResponseBody as HttpBody>::Error: Into<BoxError> + Send,
{
pub async fn resolve_service_descriptor_registry(
/// Asks the reflection service for the file containing the requested symbol (e.g., `my.package.MyService`).
///
/// **Recursive Resolution**:
/// - The server returns a `FileDescriptorProto`.
/// - The client inspects the imports (dependencies) of that file.
/// - It recursively requests any missing dependencies until the full `FileDescriptorSet` is built.
///
/// # Returns
///
/// * `Ok(fd_set)` - Successful reflection requests execution.
/// * `Err(ReflectionResolveError)` - Failed to request file descriptors to the reflection service.
pub async fn file_descriptor_set_by_symbol(
&mut self,
service_name: &str,
) -> Result<DescriptorRegistry, ReflectionResolveError> {
) -> Result<FileDescriptorSet, ReflectionResolveError> {
// Initialize Stream
let (tx, rx) = mpsc::channel(100);
@ -133,7 +140,7 @@ where
file: file_map.into_values().collect(),
};
Ok(DescriptorRegistry::from_file_descriptor_set(fd_set)?)
Ok(fd_set)
}
async fn collect_descriptors(

View file

@ -1,4 +1,5 @@
use crate::core::reflection::{
DescriptorRegistry,
client::{
ReflectionClient, ReflectionResolveError, integration_test::dummy_service::DummyEchoService,
},
@ -28,10 +29,13 @@ fn setup_reflection_client()
async fn test_reflection_client_fetches_unary_echo() {
let mut client = setup_reflection_client();
let registry = client
.resolve_service_descriptor_registry("echo.EchoService")
let fd_set = client
.file_descriptor_set_by_symbol("echo.EchoService")
.await
.expect("Failed to resolve service descriptor registry");
.expect("Failed to fetch file descriptor set by symbol");
let registry = DescriptorRegistry::from_file_descriptor_set(fd_set)
.expect("Failed to build descriptor registry");
let method = registry
.get_method_descriptor("echo.EchoService", "UnaryEcho")
@ -56,10 +60,13 @@ async fn test_reflection_client_fetches_unary_echo() {
async fn test_reflection_client_fetches_server_streaming_echo() {
let mut client = setup_reflection_client();
let registry = client
.resolve_service_descriptor_registry("echo.EchoService")
let fd_set = client
.file_descriptor_set_by_symbol("echo.EchoService")
.await
.expect("Failed to resolve service descriptor registry");
.expect("Failed to fetch file descriptor set by symbol");
let registry = DescriptorRegistry::from_file_descriptor_set(fd_set)
.expect("Failed to build descriptor registry");
let method = registry
.get_method_descriptor("echo.EchoService", "ServerStreamingEcho")
@ -84,10 +91,13 @@ async fn test_reflection_client_fetches_server_streaming_echo() {
async fn test_reflection_client_fetches_client_streaming_echo() {
let mut client = setup_reflection_client();
let registry = client
.resolve_service_descriptor_registry("echo.EchoService")
let fd_set = client
.file_descriptor_set_by_symbol("echo.EchoService")
.await
.expect("Failed to resolve service descriptor registry");
.expect("Failed to fetch file descriptor set by symbol");
let registry = DescriptorRegistry::from_file_descriptor_set(fd_set)
.expect("Failed to build descriptor registry");
let method = registry
.get_method_descriptor("echo.EchoService", "ClientStreamingEcho")
@ -112,10 +122,13 @@ async fn test_reflection_client_fetches_client_streaming_echo() {
async fn test_reflection_client_fetches_bidirectional_echo() {
let mut client = setup_reflection_client();
let registry = client
.resolve_service_descriptor_registry("echo.EchoService")
let fd_set = client
.file_descriptor_set_by_symbol("echo.EchoService")
.await
.expect("Failed to resolve service descriptor registry");
.expect("Failed to fetch file descriptor set by symbol");
let registry = DescriptorRegistry::from_file_descriptor_set(fd_set)
.expect("Failed to build descriptor registry");
let method = registry
.get_method_descriptor("echo.EchoService", "BidirectionalEcho")
@ -139,7 +152,7 @@ async fn test_reflection_service_not_found_error() {
let mut client = setup_reflection_client();
let result: Result<_, _> = client
.resolve_service_descriptor_registry("non.existent.Service")
.file_descriptor_set_by_symbol("non.existent.Service")
.await;
assert!(matches!(
@ -161,7 +174,7 @@ async fn test_server_does_not_support_reflection() {
// The client will attempt to call `/grpc.reflection.v1.ServerReflection/ServerReflectionInfo` on this service.
let result = client
.resolve_service_descriptor_registry("echo.EchoService")
.file_descriptor_set_by_symbol("echo.EchoService")
.await;
match result {