mirror of
https://codeberg.org/JasterV/granc.git
synced 2026-04-26 18:40:05 +00:00
refactor: make state implementations cleaner
This commit is contained in:
parent
08107a2faa
commit
5fc3a68ba1
5 changed files with 116 additions and 104 deletions
|
|
@ -35,9 +35,12 @@
|
|||
pub mod offline;
|
||||
pub mod online;
|
||||
pub mod online_without_reflection;
|
||||
mod types;
|
||||
|
||||
pub use types::*;
|
||||
|
||||
use crate::{grpc::client::GrpcClient, reflection::client::ReflectionClient};
|
||||
use prost_reflect::{DescriptorPool, EnumDescriptor, MessageDescriptor, ServiceDescriptor};
|
||||
use prost_reflect::DescriptorPool;
|
||||
use std::fmt::Debug;
|
||||
use tonic::transport::Channel;
|
||||
|
||||
|
|
@ -49,6 +52,12 @@ pub struct GrancClient<T> {
|
|||
state: T,
|
||||
}
|
||||
|
||||
impl<T> GrancClient<T> {
|
||||
pub(crate) fn new(state: T) -> Self {
|
||||
Self { state }
|
||||
}
|
||||
}
|
||||
|
||||
/// State: Connected to server, Schema from Server Reflection.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Online<S = Channel> {
|
||||
|
|
@ -63,69 +72,36 @@ pub struct OnlineWithoutReflection<S = Channel> {
|
|||
pool: DescriptorPool,
|
||||
}
|
||||
|
||||
impl<S> OnlineWithoutReflection<S> {
|
||||
pub(crate) fn new(grpc_client: GrpcClient<S>, pool: DescriptorPool) -> Self {
|
||||
Self { pool, grpc_client }
|
||||
}
|
||||
}
|
||||
|
||||
/// State: Disconnected, Schema from local FileDescriptor.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Offline {
|
||||
pool: DescriptorPool,
|
||||
}
|
||||
|
||||
/// A request object encapsulating all necessary information to perform a dynamic gRPC call.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DynamicRequest {
|
||||
/// The JSON body of the request.
|
||||
/// - For Unary/ServerStreaming: An Object `{}`.
|
||||
/// - For ClientStreaming/Bidirectional: An Array of Objects `[{}]`.
|
||||
pub body: serde_json::Value,
|
||||
/// Custom gRPC metadata (headers) to attach to the request.
|
||||
pub headers: Vec<(String, String)>,
|
||||
/// The fully qualified name of the service (e.g., `my.package.Service`).
|
||||
pub service: String,
|
||||
/// The name of the method to call (e.g., `SayHello`).
|
||||
pub method: String,
|
||||
}
|
||||
|
||||
/// The result of a dynamic gRPC call.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum DynamicResponse {
|
||||
/// A single response message (for Unary and Client Streaming calls).
|
||||
Unary(Result<serde_json::Value, tonic::Status>),
|
||||
/// A stream of response messages (for Server Streaming and Bidirectional calls).
|
||||
Streaming(Result<Vec<Result<serde_json::Value, tonic::Status>>, tonic::Status>),
|
||||
}
|
||||
|
||||
/// A generic wrapper for different types of Protobuf descriptors.
|
||||
///
|
||||
/// This enum allows the client to return a single type when resolving symbols,
|
||||
/// regardless of whether the symbol points to a Service, a Message, or an Enum.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Descriptor {
|
||||
MessageDescriptor(MessageDescriptor),
|
||||
ServiceDescriptor(ServiceDescriptor),
|
||||
EnumDescriptor(EnumDescriptor),
|
||||
}
|
||||
|
||||
impl Descriptor {
|
||||
/// Returns the inner [`MessageDescriptor`] if this variant is `MessageDescriptor`.
|
||||
pub fn message_descriptor(&self) -> Option<&MessageDescriptor> {
|
||||
match self {
|
||||
Descriptor::MessageDescriptor(d) => Some(d),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the inner [`ServiceDescriptor`] if this variant is `ServiceDescriptor`.
|
||||
pub fn service_descriptor(&self) -> Option<&ServiceDescriptor> {
|
||||
match self {
|
||||
Descriptor::ServiceDescriptor(d) => Some(d),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the inner [`EnumDescriptor`] if this variant is `EnumDescriptor`.
|
||||
pub fn enum_descriptor(&self) -> Option<&EnumDescriptor> {
|
||||
match self {
|
||||
Descriptor::EnumDescriptor(d) => Some(d),
|
||||
_ => None,
|
||||
}
|
||||
impl Offline {
|
||||
pub(crate) fn new(pool: DescriptorPool) -> Self {
|
||||
Self { pool }
|
||||
}
|
||||
}
|
||||
|
||||
pub trait OfflineReflectionState {
|
||||
fn descriptor_pool(&self) -> &DescriptorPool;
|
||||
}
|
||||
|
||||
impl OfflineReflectionState for Offline {
|
||||
fn descriptor_pool(&self) -> &DescriptorPool {
|
||||
&self.pool
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> OfflineReflectionState for OnlineWithoutReflection<S> {
|
||||
fn descriptor_pool(&self) -> &DescriptorPool {
|
||||
&self.pool
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@
|
|||
//! `DescriptorPool` but is **not connected** to any gRPC server.
|
||||
//!
|
||||
//! In this state, the client is strictly limited to introspection tasks.
|
||||
use super::{Descriptor, GrancClient, Offline};
|
||||
use super::{GrancClient, Offline};
|
||||
use crate::client::{OfflineReflectionState, types::Descriptor};
|
||||
use prost_reflect::{DescriptorError, DescriptorPool};
|
||||
|
||||
impl GrancClient<Offline> {
|
||||
|
|
@ -28,7 +29,12 @@ impl GrancClient<Offline> {
|
|||
state: Offline { pool },
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> GrancClient<T>
|
||||
where
|
||||
T: OfflineReflectionState,
|
||||
{
|
||||
/// Lists all services defined in the local `DescriptorPool`.
|
||||
///
|
||||
/// # Returns
|
||||
|
|
@ -36,7 +42,7 @@ impl GrancClient<Offline> {
|
|||
/// A list of fully qualified service names (e.g. `helloworld.Greeter`).
|
||||
pub fn list_services(&self) -> Vec<String> {
|
||||
self.state
|
||||
.pool
|
||||
.descriptor_pool()
|
||||
.services()
|
||||
.map(|s| s.full_name().to_string())
|
||||
.collect()
|
||||
|
|
@ -53,7 +59,7 @@ impl GrancClient<Offline> {
|
|||
/// * `Some(Descriptor)` - The resolved descriptor if found.
|
||||
/// * `None` - If the symbol does not exist in the pool.
|
||||
pub fn get_descriptor_by_symbol(&self, symbol: &str) -> Option<Descriptor> {
|
||||
let pool = &self.state.pool;
|
||||
let pool = self.state.descriptor_pool();
|
||||
|
||||
if let Some(descriptor) = pool.get_service_by_name(symbol) {
|
||||
return Some(Descriptor::ServiceDescriptor(descriptor));
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use super::{
|
|||
};
|
||||
use crate::{
|
||||
BoxError,
|
||||
client::Offline,
|
||||
grpc::client::GrpcClient,
|
||||
reflection::client::{ReflectionClient, ReflectionResolveError},
|
||||
};
|
||||
|
|
@ -103,7 +104,11 @@ where
|
|||
file_descriptor: Vec<u8>,
|
||||
) -> Result<GrancClient<OnlineWithoutReflection<S>>, DescriptorError> {
|
||||
let pool = DescriptorPool::decode(file_descriptor.as_slice())?;
|
||||
Ok(GrancClient::new(self.state.grpc_client, pool))
|
||||
|
||||
Ok(GrancClient::new(OnlineWithoutReflection::new(
|
||||
self.state.grpc_client,
|
||||
pool,
|
||||
)))
|
||||
}
|
||||
|
||||
/// Lists services available on the server via Reflection.
|
||||
|
|
@ -134,9 +139,7 @@ where
|
|||
|
||||
let pool = DescriptorPool::from_file_descriptor_set(fd_set)?;
|
||||
|
||||
// Build a temporary OnlineWithoutReflection client just to reuse lookup logic
|
||||
let mut client =
|
||||
GrancClient::<OnlineWithoutReflection<S>>::new(self.state.grpc_client.clone(), pool);
|
||||
let client = GrancClient::new(Offline::new(pool));
|
||||
|
||||
client
|
||||
.get_descriptor_by_symbol(symbol)
|
||||
|
|
@ -159,8 +162,11 @@ where
|
|||
.await?;
|
||||
let pool = DescriptorPool::from_file_descriptor_set(fd_set)?;
|
||||
|
||||
let mut client =
|
||||
GrancClient::<OnlineWithoutReflection<S>>::new(self.state.grpc_client.clone(), pool);
|
||||
let mut client = GrancClient::new(OnlineWithoutReflection::new(
|
||||
self.state.grpc_client.clone(),
|
||||
pool,
|
||||
));
|
||||
|
||||
Ok(client.dynamic(request).await?)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,14 +2,10 @@
|
|||
//!
|
||||
//! This module defines the `GrancClient` behavior when it is connected to a server
|
||||
//! but uses a local, in-memory `DescriptorPool` (Static schema) to resolve messages.
|
||||
use super::{Descriptor, DynamicRequest, DynamicResponse, GrancClient, OnlineWithoutReflection};
|
||||
use crate::{
|
||||
BoxError,
|
||||
grpc::client::{GrpcClient, GrpcRequestError},
|
||||
};
|
||||
use super::{DynamicRequest, DynamicResponse, GrancClient, OnlineWithoutReflection};
|
||||
use crate::{BoxError, client::OfflineReflectionState, grpc::client::GrpcRequestError};
|
||||
use futures_util::{Stream, StreamExt};
|
||||
use http_body::Body as HttpBody;
|
||||
use prost_reflect::DescriptorPool;
|
||||
use std::fmt::Debug;
|
||||
|
||||
/// Errors that can occur during a dynamic call in OnlineWithoutReflection mode.
|
||||
|
|
@ -25,47 +21,12 @@ pub enum DynamicCallError {
|
|||
GrpcRequestError(#[from] GrpcRequestError),
|
||||
}
|
||||
|
||||
impl<S> GrancClient<OnlineWithoutReflection<S>>
|
||||
where
|
||||
S: Clone,
|
||||
{
|
||||
pub(crate) fn new(grpc_client: GrpcClient<S>, pool: DescriptorPool) -> Self {
|
||||
Self {
|
||||
state: OnlineWithoutReflection { grpc_client, pool },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> GrancClient<OnlineWithoutReflection<S>>
|
||||
where
|
||||
S: tonic::client::GrpcService<tonic::body::Body> + Clone,
|
||||
S::ResponseBody: HttpBody<Data = tonic::codegen::Bytes> + Send + 'static,
|
||||
<S::ResponseBody as HttpBody>::Error: Into<BoxError> + Send,
|
||||
{
|
||||
/// Lists all services in the local descriptor pool.
|
||||
pub fn list_services(&mut self) -> Vec<String> {
|
||||
self.state
|
||||
.pool
|
||||
.services()
|
||||
.map(|s| s.full_name().to_string())
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Looks up a symbol in the local descriptor pool.
|
||||
pub fn get_descriptor_by_symbol(&mut self, symbol: &str) -> Option<Descriptor> {
|
||||
let pool = &self.state.pool;
|
||||
if let Some(descriptor) = pool.get_service_by_name(symbol) {
|
||||
return Some(Descriptor::ServiceDescriptor(descriptor));
|
||||
}
|
||||
if let Some(descriptor) = pool.get_message_by_name(symbol) {
|
||||
return Some(Descriptor::MessageDescriptor(descriptor));
|
||||
}
|
||||
if let Some(descriptor) = pool.get_enum_by_name(symbol) {
|
||||
return Some(Descriptor::EnumDescriptor(descriptor));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Executes a dynamic gRPC request using the local descriptor pool.
|
||||
pub async fn dynamic(
|
||||
&mut self,
|
||||
|
|
@ -73,7 +34,7 @@ where
|
|||
) -> Result<DynamicResponse, DynamicCallError> {
|
||||
let method = self
|
||||
.state
|
||||
.pool
|
||||
.descriptor_pool()
|
||||
.get_service_by_name(&request.service)
|
||||
.ok_or_else(|| DynamicCallError::ServiceNotFound(request.service.clone()))?
|
||||
.methods()
|
||||
|
|
|
|||
63
granc-core/src/client/types.rs
Normal file
63
granc-core/src/client/types.rs
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
use prost_reflect::{EnumDescriptor, MessageDescriptor, ServiceDescriptor};
|
||||
use std::fmt::Debug;
|
||||
|
||||
/// A request object encapsulating all necessary information to perform a dynamic gRPC call.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DynamicRequest {
|
||||
/// The JSON body of the request.
|
||||
/// - For Unary/ServerStreaming: An Object `{}`.
|
||||
/// - For ClientStreaming/Bidirectional: An Array of Objects `[{}]`.
|
||||
pub body: serde_json::Value,
|
||||
/// Custom gRPC metadata (headers) to attach to the request.
|
||||
pub headers: Vec<(String, String)>,
|
||||
/// The fully qualified name of the service (e.g., `my.package.Service`).
|
||||
pub service: String,
|
||||
/// The name of the method to call (e.g., `SayHello`).
|
||||
pub method: String,
|
||||
}
|
||||
|
||||
/// The result of a dynamic gRPC call.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum DynamicResponse {
|
||||
/// A single response message (for Unary and Client Streaming calls).
|
||||
Unary(Result<serde_json::Value, tonic::Status>),
|
||||
/// A stream of response messages (for Server Streaming and Bidirectional calls).
|
||||
Streaming(Result<Vec<Result<serde_json::Value, tonic::Status>>, tonic::Status>),
|
||||
}
|
||||
|
||||
/// A generic wrapper for different types of Protobuf descriptors.
|
||||
///
|
||||
/// This enum allows the client to return a single type when resolving symbols,
|
||||
/// regardless of whether the symbol points to a Service, a Message, or an Enum.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Descriptor {
|
||||
MessageDescriptor(MessageDescriptor),
|
||||
ServiceDescriptor(ServiceDescriptor),
|
||||
EnumDescriptor(EnumDescriptor),
|
||||
}
|
||||
|
||||
impl Descriptor {
|
||||
/// Returns the inner [`MessageDescriptor`] if this variant is `MessageDescriptor`.
|
||||
pub fn message_descriptor(&self) -> Option<&MessageDescriptor> {
|
||||
match self {
|
||||
Descriptor::MessageDescriptor(d) => Some(d),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the inner [`ServiceDescriptor`] if this variant is `ServiceDescriptor`.
|
||||
pub fn service_descriptor(&self) -> Option<&ServiceDescriptor> {
|
||||
match self {
|
||||
Descriptor::ServiceDescriptor(d) => Some(d),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the inner [`EnumDescriptor`] if this variant is `EnumDescriptor`.
|
||||
pub fn enum_descriptor(&self) -> Option<&EnumDescriptor> {
|
||||
match self {
|
||||
Descriptor::EnumDescriptor(d) => Some(d),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue