Add idiomatic documentation to Go, Java, Python, and Rust clients
This commit is contained in:
@@ -1,3 +1,7 @@
|
||||
//! API-key wrapper and the `tonic` interceptor that attaches it as a Bearer
|
||||
//! token on every outbound gRPC call. The wrapper redacts its inner value in
|
||||
//! `Debug`/`Display` so logs never leak the secret.
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use tonic::metadata::MetadataValue;
|
||||
@@ -5,14 +9,21 @@ use tonic::service::Interceptor;
|
||||
use tonic::{Request, Status};
|
||||
|
||||
/// API key wrapper that avoids exposing raw credentials in formatted output.
|
||||
///
|
||||
/// Use [`ApiKey::expose_secret`] when the underlying string is genuinely
|
||||
/// needed (for example, building the `authorization` header).
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct ApiKey(String);
|
||||
|
||||
impl ApiKey {
|
||||
/// Construct an [`ApiKey`] from the raw `mxgw_<key-id>_<secret>` string
|
||||
/// returned by the gateway's `apikey` admin command.
|
||||
pub fn new(value: impl Into<String>) -> Self {
|
||||
Self(value.into())
|
||||
}
|
||||
|
||||
/// Return the raw key value. Callers must not log or otherwise persist
|
||||
/// the result.
|
||||
pub fn expose_secret(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
@@ -40,6 +51,9 @@ pub struct AuthInterceptor {
|
||||
}
|
||||
|
||||
impl AuthInterceptor {
|
||||
/// Build an interceptor that injects the supplied API key on every
|
||||
/// request. Pass `None` to disable authentication (useful for local
|
||||
/// development against a gateway with `Authentication:Required = false`).
|
||||
pub fn new(api_key: Option<ApiKey>) -> Self {
|
||||
Self { api_key }
|
||||
}
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
//! High-level wrapper around the generated `MxAccessGateway` gRPC client.
|
||||
//!
|
||||
//! [`GatewayClient::connect`] builds an authenticated `tonic` channel using
|
||||
//! the supplied [`ClientOptions`], applies the bearer-token interceptor, and
|
||||
//! exposes typed methods for the unary and streaming RPCs. Most application
|
||||
//! code should prefer [`GatewayClient::open_session`] and the [`Session`]
|
||||
//! handle it returns, rather than the `*_raw` methods.
|
||||
|
||||
use std::fs;
|
||||
|
||||
use tonic::codegen::InterceptedService;
|
||||
@@ -14,11 +22,21 @@ use crate::generated::mxaccess_gateway::v1::{
|
||||
use crate::options::ClientOptions;
|
||||
use crate::session::Session;
|
||||
|
||||
/// Generated gateway client wrapped in the auth interceptor that
|
||||
/// [`GatewayClient`] uses internally.
|
||||
pub type RawGatewayClient = MxAccessGatewayClient<InterceptedService<Channel, AuthInterceptor>>;
|
||||
|
||||
/// Pinned, boxed [`MxEvent`] stream returned by
|
||||
/// [`GatewayClient::stream_events`]. Errors are pre-mapped from
|
||||
/// `tonic::Status` to [`Error`]; dropping the stream cancels the call.
|
||||
pub type EventStream =
|
||||
std::pin::Pin<Box<dyn futures_core::Stream<Item = Result<MxEvent, Error>> + Send + 'static>>;
|
||||
|
||||
/// Thin owner for the generated gateway client.
|
||||
/// Thin async wrapper around the generated gateway client.
|
||||
///
|
||||
/// The wrapper is `Clone`: every clone shares the underlying tonic channel
|
||||
/// (cheap, reference-counted) and the same call/stream timeouts. It is
|
||||
/// designed to be cheap enough to clone per request handler.
|
||||
#[derive(Clone)]
|
||||
pub struct GatewayClient {
|
||||
inner: RawGatewayClient,
|
||||
@@ -27,6 +45,13 @@ pub struct GatewayClient {
|
||||
}
|
||||
|
||||
impl GatewayClient {
|
||||
/// Connect to the gateway endpoint described by `options` and return a
|
||||
/// ready-to-use client.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns [`Error::InvalidEndpoint`] if the endpoint URL or CA file is
|
||||
/// malformed, and [`Error::Transport`] if the TCP/TLS handshake fails.
|
||||
pub async fn connect(options: ClientOptions) -> Result<Self, Error> {
|
||||
let mut endpoint =
|
||||
Channel::from_shared(options.endpoint().to_owned()).map_err(|source| {
|
||||
@@ -62,18 +87,30 @@ impl GatewayClient {
|
||||
})
|
||||
}
|
||||
|
||||
/// Borrow the underlying generated client. Use this only when you need
|
||||
/// access to RPCs not surfaced by the wrapper.
|
||||
pub fn raw_client(&mut self) -> &mut RawGatewayClient {
|
||||
&mut self.inner
|
||||
}
|
||||
|
||||
/// Consume the wrapper and return the underlying generated client.
|
||||
pub fn into_inner(self) -> RawGatewayClient {
|
||||
self.inner
|
||||
}
|
||||
|
||||
/// Build a [`Session`] handle from a previously opened session id. No
|
||||
/// RPC is performed — this is the cheap counterpart to
|
||||
/// [`GatewayClient::open_session`] for callers that already own the id.
|
||||
pub fn session(&self, session_id: impl Into<String>) -> Session {
|
||||
Session::new(session_id, self.clone())
|
||||
}
|
||||
|
||||
/// Issue an `OpenSession` RPC and return the raw reply without
|
||||
/// validating its `protocol_status`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns the `tonic::Status` mapped through [`Error::from`].
|
||||
pub async fn open_session_raw(
|
||||
&self,
|
||||
request: OpenSessionRequest,
|
||||
@@ -83,12 +120,25 @@ impl GatewayClient {
|
||||
Ok(response.into_inner())
|
||||
}
|
||||
|
||||
/// Open a session, validate its `protocol_status`, and return a typed
|
||||
/// [`Session`] handle bound to this client.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns [`Error::ProtocolStatus`] if the gateway accepts the call
|
||||
/// but reports a non-OK protocol status, plus any of the
|
||||
/// [`Error`] variants produced by [`open_session_raw`](Self::open_session_raw).
|
||||
pub async fn open_session(&self, request: OpenSessionRequest) -> Result<Session, Error> {
|
||||
let reply = self.open_session_raw(request).await?;
|
||||
ensure_protocol_success("open session", reply.protocol_status.as_ref())?;
|
||||
Ok(Session::new(reply.session_id, self.clone()))
|
||||
}
|
||||
|
||||
/// Issue a `CloseSession` RPC and return the raw reply.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns the `tonic::Status` mapped through [`Error::from`].
|
||||
pub async fn close_session_raw(
|
||||
&self,
|
||||
request: CloseSessionRequest,
|
||||
@@ -98,16 +148,39 @@ impl GatewayClient {
|
||||
Ok(response.into_inner())
|
||||
}
|
||||
|
||||
/// Issue an `Invoke` RPC and return the raw reply, even when the
|
||||
/// command-level protocol status is non-OK.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns the `tonic::Status` mapped through [`Error::from`].
|
||||
pub async fn invoke_raw(&self, request: MxCommandRequest) -> Result<MxCommandReply, Error> {
|
||||
let mut client = self.inner.clone();
|
||||
let response = client.invoke(self.unary_request(request)).await?;
|
||||
Ok(response.into_inner())
|
||||
}
|
||||
|
||||
/// Issue an `Invoke` RPC and surface a non-OK reply as
|
||||
/// [`Error::Command`].
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns [`Error::Command`] when the reply's `protocol_status` is not
|
||||
/// `Ok`, plus any errors propagated by
|
||||
/// [`invoke_raw`](Self::invoke_raw).
|
||||
pub async fn invoke(&self, request: MxCommandRequest) -> Result<MxCommandReply, Error> {
|
||||
ensure_command_success(self.invoke_raw(request).await?)
|
||||
}
|
||||
|
||||
/// Open the server-streaming `StreamEvents` RPC.
|
||||
///
|
||||
/// The returned [`EventStream`] yields `MxEvent` messages as the worker
|
||||
/// produces them. Dropping the stream cancels the gRPC call cooperatively.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns the `tonic::Status` mapped through [`Error::from`] if the
|
||||
/// server rejects the subscription.
|
||||
pub async fn stream_events(&self, request: StreamEventsRequest) -> Result<EventStream, Error> {
|
||||
let mut client = self.inner.clone();
|
||||
let response = client.stream_events(self.stream_request(request)).await?;
|
||||
|
||||
@@ -1,75 +1,136 @@
|
||||
//! Error types surfaced by the Rust client.
|
||||
//!
|
||||
//! [`Error`] is the umbrella enum returned by every async wrapper. It
|
||||
//! classifies `tonic::Status` codes (auth, timeout, cancellation) and folds
|
||||
//! gateway protocol failures and command-level rejections into structured
|
||||
//! variants. Credentials embedded in status messages are scrubbed before the
|
||||
//! message reaches a caller.
|
||||
|
||||
use thiserror::Error as ThisError;
|
||||
use tonic::Code;
|
||||
|
||||
use crate::generated::mxaccess_gateway::v1::{MxCommandReply, ProtocolStatus, ProtocolStatusCode};
|
||||
|
||||
/// Top-level error type returned by the Rust client wrappers.
|
||||
///
|
||||
/// The variants distinguish transport/setup failures, classified gRPC status
|
||||
/// codes, gateway protocol-level failures (`OpenSession`, `CloseSession`),
|
||||
/// and command-level rejections that surface a populated [`MxCommandReply`].
|
||||
#[derive(Debug, ThisError)]
|
||||
pub enum Error {
|
||||
/// Endpoint URL could not be parsed or its TLS material could not be
|
||||
/// loaded.
|
||||
#[error("invalid gateway endpoint `{endpoint}`: {detail}")]
|
||||
InvalidEndpoint { endpoint: String, detail: String },
|
||||
InvalidEndpoint {
|
||||
/// Endpoint string supplied by the caller.
|
||||
endpoint: String,
|
||||
/// Human-readable explanation of the parse/load failure.
|
||||
detail: String,
|
||||
},
|
||||
|
||||
/// A caller-provided argument failed local validation before any RPC
|
||||
/// was dispatched (for example, a bulk command exceeding the size cap).
|
||||
#[error("invalid argument `{name}`: {detail}")]
|
||||
InvalidArgument { name: String, detail: String },
|
||||
InvalidArgument {
|
||||
/// Name of the offending argument.
|
||||
name: String,
|
||||
/// Reason the argument was rejected.
|
||||
detail: String,
|
||||
},
|
||||
|
||||
/// Tonic transport-level failure (DNS, connect, TLS handshake).
|
||||
#[error("gateway transport error: {0}")]
|
||||
Transport(#[from] tonic::transport::Error),
|
||||
|
||||
/// Server returned `Unauthenticated` — the API key was missing or
|
||||
/// rejected.
|
||||
#[error("authentication failed: {message}")]
|
||||
Authentication {
|
||||
/// Redacted server-supplied detail message.
|
||||
message: String,
|
||||
/// Original `tonic::Status` for callers that need the full context.
|
||||
#[source]
|
||||
status: Box<tonic::Status>,
|
||||
},
|
||||
|
||||
/// Server returned `PermissionDenied` — the API key is valid but lacks
|
||||
/// the required scope.
|
||||
#[error("authorization failed: {message}")]
|
||||
Authorization {
|
||||
/// Redacted server-supplied detail message.
|
||||
message: String,
|
||||
/// Original `tonic::Status`.
|
||||
#[source]
|
||||
status: Box<tonic::Status>,
|
||||
},
|
||||
|
||||
/// Server returned `DeadlineExceeded`. Usually the per-call deadline
|
||||
/// configured via [`crate::options::ClientOptions::with_call_timeout`].
|
||||
#[error("gateway call timed out: {message}")]
|
||||
Timeout {
|
||||
/// Redacted server-supplied detail message.
|
||||
message: String,
|
||||
/// Original `tonic::Status`.
|
||||
#[source]
|
||||
status: Box<tonic::Status>,
|
||||
},
|
||||
|
||||
/// Server (or client) cancelled the call before a reply was produced.
|
||||
#[error("gateway call cancelled: {message}")]
|
||||
Cancelled {
|
||||
/// Redacted server-supplied detail message.
|
||||
message: String,
|
||||
/// Original `tonic::Status`.
|
||||
#[source]
|
||||
status: Box<tonic::Status>,
|
||||
},
|
||||
|
||||
/// Any other `tonic::Status` that did not match a more specific variant.
|
||||
#[error("gateway status error: {0}")]
|
||||
Status(Box<tonic::Status>),
|
||||
|
||||
/// Gateway accepted the call but the worker reply carried a non-OK
|
||||
/// protocol status. The wrapped [`CommandError`] preserves the full
|
||||
/// reply so callers can inspect the worker's status payload.
|
||||
#[error("gateway command failed: {0}")]
|
||||
Command(#[from] Box<CommandError>),
|
||||
|
||||
/// Protocol-level operation (open/close session) returned a non-OK
|
||||
/// [`ProtocolStatus`] envelope.
|
||||
#[error("gateway {operation} failed: {code:?}: {message}")]
|
||||
ProtocolStatus {
|
||||
/// Operation name, e.g. `"open session"`.
|
||||
operation: &'static str,
|
||||
/// Decoded protocol status code from the server.
|
||||
code: ProtocolStatusCode,
|
||||
/// Detail message from the server.
|
||||
message: String,
|
||||
},
|
||||
}
|
||||
|
||||
/// Wrapper around an [`MxCommandReply`] whose `protocol_status` reported a
|
||||
/// non-OK code.
|
||||
///
|
||||
/// The wrapper is heap-allocated inside [`Error::Command`] to keep the
|
||||
/// containing enum small. Callers can recover the reply with
|
||||
/// [`CommandError::reply`] or [`CommandError::into_reply`].
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CommandError {
|
||||
reply: MxCommandReply,
|
||||
}
|
||||
|
||||
impl CommandError {
|
||||
/// Wrap an already-failed command reply.
|
||||
pub fn new(reply: MxCommandReply) -> Self {
|
||||
Self { reply }
|
||||
}
|
||||
|
||||
/// Borrow the underlying reply (correlation id, status, payload).
|
||||
pub fn reply(&self) -> &MxCommandReply {
|
||||
&self.reply
|
||||
}
|
||||
|
||||
/// Consume the error and return the underlying reply.
|
||||
pub fn into_reply(self) -> MxCommandReply {
|
||||
self.reply
|
||||
}
|
||||
@@ -118,6 +179,13 @@ impl From<tonic::Status> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
/// Promote a non-OK protocol status carried inside an [`MxCommandReply`]
|
||||
/// to an [`Error::Command`].
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns [`Error::Command`] when `reply.protocol_status` is missing or
|
||||
/// reports any code other than [`ProtocolStatusCode::Ok`].
|
||||
pub fn ensure_command_success(reply: MxCommandReply) -> Result<MxCommandReply, Error> {
|
||||
let code = reply
|
||||
.protocol_status
|
||||
@@ -132,6 +200,13 @@ pub fn ensure_command_success(reply: MxCommandReply) -> Result<MxCommandReply, E
|
||||
}
|
||||
}
|
||||
|
||||
/// Validate a [`ProtocolStatus`] envelope returned by an open/close-session
|
||||
/// reply.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns [`Error::ProtocolStatus`] tagged with `operation` when `status`
|
||||
/// is missing or reports any code other than [`ProtocolStatusCode::Ok`].
|
||||
pub fn ensure_protocol_success(
|
||||
operation: &'static str,
|
||||
status: Option<&ProtocolStatus>,
|
||||
|
||||
@@ -1,4 +1,17 @@
|
||||
//! Generated tonic/prost bindings for the gateway, worker, and Galaxy
|
||||
//! Repository protobuf contracts.
|
||||
//!
|
||||
//! Modules under this namespace are produced by `tonic-build` from the shared
|
||||
//! `.proto` files in the contracts project. Treat them as build output: do not
|
||||
//! hand-edit, and prefer the wrappers in [`crate::client`], [`crate::session`],
|
||||
//! [`crate::galaxy`], and [`crate::value`] for application code.
|
||||
|
||||
#![allow(missing_docs)]
|
||||
|
||||
/// Generated bindings for the public `mxaccess_gateway` gRPC service.
|
||||
pub mod mxaccess_gateway {
|
||||
/// `mxaccess_gateway.v1` package — the v1 wire contract surfaced by the
|
||||
/// gateway to language clients.
|
||||
pub mod v1 {
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
|
||||
@@ -6,7 +19,10 @@ pub mod mxaccess_gateway {
|
||||
}
|
||||
}
|
||||
|
||||
/// Generated bindings for the internal gateway↔worker IPC protocol.
|
||||
pub mod mxaccess_worker {
|
||||
/// `mxaccess_worker.v1` package — frame and envelope types used inside
|
||||
/// the named-pipe transport between gateway and worker.
|
||||
pub mod v1 {
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
|
||||
@@ -14,7 +30,10 @@ pub mod mxaccess_worker {
|
||||
}
|
||||
}
|
||||
|
||||
/// Generated bindings for the Galaxy Repository read-only browse service.
|
||||
pub mod galaxy_repository {
|
||||
/// `galaxy_repository.v1` package — types for the Galaxy hierarchy
|
||||
/// discovery and deploy-event watch RPCs.
|
||||
pub mod v1 {
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
|
||||
|
||||
+18
-3
@@ -1,8 +1,15 @@
|
||||
//! Rust client scaffold for MXAccess Gateway.
|
||||
//! Rust client for the MXAccess Gateway.
|
||||
//!
|
||||
//! The crate compiles generated `tonic` bindings from the shared gateway
|
||||
//! protobuf contracts and exposes a small handwritten surface for future client
|
||||
//! implementation work.
|
||||
//! protobuf contracts and exposes a small handwritten surface — connection
|
||||
//! options, an authentication interceptor, gateway and Galaxy Repository
|
||||
//! clients, an `MxValue` builder/projection, and protocol-error types — that
|
||||
//! application code can use without touching the generated modules directly.
|
||||
//!
|
||||
//! See the project root `RustClientDesign.md` for the design rationale and
|
||||
//! the cross-language client matrix.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
pub mod auth;
|
||||
pub mod client;
|
||||
@@ -14,11 +21,19 @@ pub mod session;
|
||||
pub mod value;
|
||||
pub mod version;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use auth::{ApiKey, AuthInterceptor};
|
||||
#[doc(inline)]
|
||||
pub use client::{EventStream, GatewayClient};
|
||||
#[doc(inline)]
|
||||
pub use error::{CommandError, Error};
|
||||
#[doc(inline)]
|
||||
pub use galaxy::{DeployEventStream, GalaxyClient};
|
||||
#[doc(inline)]
|
||||
pub use options::ClientOptions;
|
||||
#[doc(inline)]
|
||||
pub use session::Session;
|
||||
#[doc(inline)]
|
||||
pub use value::{MxArrayProjection, MxArrayValue, MxStatus, MxValue, MxValueProjection};
|
||||
#[doc(inline)]
|
||||
pub use version::{CLIENT_VERSION, GATEWAY_PROTOCOL_VERSION, WORKER_PROTOCOL_VERSION};
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
//! Connection options shared by [`crate::client::GatewayClient`] and
|
||||
//! [`crate::galaxy::GalaxyClient`]. Build with [`ClientOptions::new`] and a
|
||||
//! chain of `with_*` setters; the `Debug` impl redacts the API key.
|
||||
|
||||
use std::fmt;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::auth::ApiKey;
|
||||
|
||||
/// Configuration for connecting to a gateway endpoint.
|
||||
///
|
||||
/// Defaults are 10s connect timeout, 30s call timeout, no streaming timeout,
|
||||
/// and plaintext (h2c) transport. Set [`ClientOptions::with_plaintext`] to
|
||||
/// `false` and supply [`ClientOptions::with_ca_file`] / a server-name override
|
||||
/// for TLS deployments.
|
||||
#[derive(Clone)]
|
||||
pub struct ClientOptions {
|
||||
endpoint: String,
|
||||
@@ -17,6 +27,8 @@ pub struct ClientOptions {
|
||||
}
|
||||
|
||||
impl ClientOptions {
|
||||
/// Build options for the supplied gateway endpoint URL (for example,
|
||||
/// `http://127.0.0.1:5000`). Other settings take their defaults.
|
||||
pub fn new(endpoint: impl Into<String>) -> Self {
|
||||
Self {
|
||||
endpoint: endpoint.into(),
|
||||
@@ -30,69 +42,91 @@ impl ClientOptions {
|
||||
}
|
||||
}
|
||||
|
||||
/// Attach an API key. The key flows through [`crate::auth::AuthInterceptor`]
|
||||
/// as the Bearer token on every request.
|
||||
pub fn with_api_key(mut self, api_key: ApiKey) -> Self {
|
||||
self.api_key = Some(api_key);
|
||||
self
|
||||
}
|
||||
|
||||
/// Toggle h2c (plaintext) vs TLS. `true` (the default) skips the TLS
|
||||
/// handshake and is suitable for loopback development.
|
||||
pub fn with_plaintext(mut self, plaintext: bool) -> Self {
|
||||
self.plaintext = plaintext;
|
||||
self
|
||||
}
|
||||
|
||||
/// Trust roots PEM bundle for TLS connections. Ignored when
|
||||
/// `plaintext` is `true`.
|
||||
pub fn with_ca_file(mut self, ca_file: impl Into<PathBuf>) -> Self {
|
||||
self.ca_file = Some(ca_file.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Override the SNI/server name used during the TLS handshake. Useful
|
||||
/// when the dial-target host name does not match the certificate.
|
||||
pub fn with_server_name_override(mut self, server_name_override: impl Into<String>) -> Self {
|
||||
self.server_name_override = Some(server_name_override.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Maximum time the transport waits for the initial TCP/TLS connection.
|
||||
pub fn with_connect_timeout(mut self, connect_timeout: Duration) -> Self {
|
||||
self.connect_timeout = connect_timeout;
|
||||
self
|
||||
}
|
||||
|
||||
/// Per-call deadline applied to every unary RPC. Streaming RPCs use
|
||||
/// [`ClientOptions::with_stream_timeout`] instead.
|
||||
pub fn with_call_timeout(mut self, call_timeout: Duration) -> Self {
|
||||
self.call_timeout = call_timeout;
|
||||
self
|
||||
}
|
||||
|
||||
/// Optional deadline applied to streaming RPCs (for example,
|
||||
/// `StreamEvents`). Without a stream timeout the stream lives until the
|
||||
/// caller drops it or the server closes it.
|
||||
pub fn with_stream_timeout(mut self, stream_timeout: Duration) -> Self {
|
||||
self.stream_timeout = Some(stream_timeout);
|
||||
self
|
||||
}
|
||||
|
||||
/// Configured endpoint URL.
|
||||
pub fn endpoint(&self) -> &str {
|
||||
&self.endpoint
|
||||
}
|
||||
|
||||
/// Configured API key, if any.
|
||||
pub fn api_key(&self) -> Option<&ApiKey> {
|
||||
self.api_key.as_ref()
|
||||
}
|
||||
|
||||
/// Whether the transport runs in plaintext (h2c) mode.
|
||||
pub fn plaintext(&self) -> bool {
|
||||
self.plaintext
|
||||
}
|
||||
|
||||
/// Optional CA bundle path used to validate the server certificate.
|
||||
pub fn ca_file(&self) -> Option<&PathBuf> {
|
||||
self.ca_file.as_ref()
|
||||
}
|
||||
|
||||
/// Optional SNI / server-name override for TLS handshakes.
|
||||
pub fn server_name_override(&self) -> Option<&str> {
|
||||
self.server_name_override.as_deref()
|
||||
}
|
||||
|
||||
/// Connect timeout used during transport setup.
|
||||
pub fn connect_timeout(&self) -> Duration {
|
||||
self.connect_timeout
|
||||
}
|
||||
|
||||
/// Per-call timeout for unary RPCs.
|
||||
pub fn call_timeout(&self) -> Duration {
|
||||
self.call_timeout
|
||||
}
|
||||
|
||||
/// Optional per-call timeout for streaming RPCs.
|
||||
pub fn stream_timeout(&self) -> Option<Duration> {
|
||||
self.stream_timeout
|
||||
}
|
||||
|
||||
+139
-1
@@ -1,3 +1,13 @@
|
||||
//! Typed handle around an opened gateway session.
|
||||
//!
|
||||
//! [`Session`] wraps an `OpenSession` reply (just the session id) plus a
|
||||
//! cloned [`GatewayClient`] and offers Rust-shaped methods for the
|
||||
//! command surface that the worker exposes — `Register`, `AddItem`,
|
||||
//! bulk subscribe variants, `Write`/`Write2`, and the event stream.
|
||||
//!
|
||||
//! Bulk commands enforce a 1000-item cap before contacting the worker, in
|
||||
//! line with the gateway's documented `MAX_BULK_ITEMS`.
|
||||
|
||||
use crate::client::{EventStream, GatewayClient};
|
||||
use crate::error::{ensure_protocol_success, Error};
|
||||
use crate::generated::mxaccess_gateway::v1::mx_command::Payload;
|
||||
@@ -13,7 +23,13 @@ use crate::value::MxValue;
|
||||
|
||||
const MAX_BULK_ITEMS: usize = 1_000;
|
||||
|
||||
/// Session identifier returned by the gateway.
|
||||
/// Handle to an opened gateway session.
|
||||
///
|
||||
/// `Session` carries the gateway-issued session id and a cloned
|
||||
/// [`GatewayClient`]. All methods are async and stateless on the client
|
||||
/// side: the gateway tracks per-session worker state. Drop the handle and
|
||||
/// call [`Session::close`] to release the worker; the gateway also reaps
|
||||
/// orphaned sessions on its own schedule.
|
||||
#[derive(Clone)]
|
||||
pub struct Session {
|
||||
id: String,
|
||||
@@ -28,10 +44,18 @@ impl Session {
|
||||
}
|
||||
}
|
||||
|
||||
/// Borrow the gateway-assigned session id.
|
||||
pub fn id(&self) -> &str {
|
||||
&self.id
|
||||
}
|
||||
|
||||
/// Convenience constructor that issues `OpenSession` with the supplied
|
||||
/// client session name and returns the resulting [`Session`].
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Propagates errors from
|
||||
/// [`GatewayClient::open_session`](crate::client::GatewayClient::open_session).
|
||||
pub async fn open(client: GatewayClient, client_session_name: &str) -> Result<Self, Error> {
|
||||
client
|
||||
.open_session(OpenSessionRequest {
|
||||
@@ -41,6 +65,12 @@ impl Session {
|
||||
.await
|
||||
}
|
||||
|
||||
/// Issue `CloseSession` against this session id.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns [`Error::ProtocolStatus`] if the gateway returns a non-OK
|
||||
/// envelope and any transport/status errors propagated by tonic.
|
||||
pub async fn close(&self) -> Result<(), Error> {
|
||||
let reply = self
|
||||
.client
|
||||
@@ -53,6 +83,12 @@ impl Session {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Run MXAccess `Register` and return the assigned `ServerHandle`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns [`Error::Command`] if the worker reports a non-OK status,
|
||||
/// plus transport/status errors from tonic.
|
||||
pub async fn register(&self, client_name: &str) -> Result<i32, Error> {
|
||||
let reply = self
|
||||
.invoke(
|
||||
@@ -66,6 +102,13 @@ impl Session {
|
||||
Ok(register_server_handle(&reply))
|
||||
}
|
||||
|
||||
/// Run MXAccess `AddItem` against `server_handle` and return the
|
||||
/// assigned `ItemHandle`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns [`Error::Command`] when MXAccess rejects the item
|
||||
/// definition, plus transport/status errors from tonic.
|
||||
pub async fn add_item(&self, server_handle: i32, item_definition: &str) -> Result<i32, Error> {
|
||||
let reply = self
|
||||
.invoke(
|
||||
@@ -80,6 +123,12 @@ impl Session {
|
||||
Ok(add_item_handle(&reply))
|
||||
}
|
||||
|
||||
/// Run MXAccess `AddItem2` (item with a caller-supplied context string)
|
||||
/// and return the assigned `ItemHandle`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Same conditions as [`Session::add_item`].
|
||||
pub async fn add_item2(
|
||||
&self,
|
||||
server_handle: i32,
|
||||
@@ -100,6 +149,12 @@ impl Session {
|
||||
Ok(add_item2_handle(&reply))
|
||||
}
|
||||
|
||||
/// Run MXAccess `RemoveItem` for the given handle pair.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns [`Error::Command`] on a non-OK worker status, plus the
|
||||
/// usual transport/status errors.
|
||||
pub async fn remove_item(&self, server_handle: i32, item_handle: i32) -> Result<(), Error> {
|
||||
self.invoke(
|
||||
MxCommandKind::RemoveItem,
|
||||
@@ -112,6 +167,12 @@ impl Session {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Run MXAccess `Advise` to start receiving change notifications for
|
||||
/// the given item.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns [`Error::Command`] when the worker reports a non-OK status.
|
||||
pub async fn advise(&self, server_handle: i32, item_handle: i32) -> Result<(), Error> {
|
||||
self.invoke(
|
||||
MxCommandKind::Advise,
|
||||
@@ -124,6 +185,12 @@ impl Session {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Run MXAccess `UnAdvise` to stop change notifications for the given
|
||||
/// item.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns [`Error::Command`] when the worker reports a non-OK status.
|
||||
pub async fn un_advise(&self, server_handle: i32, item_handle: i32) -> Result<(), Error> {
|
||||
self.invoke(
|
||||
MxCommandKind::UnAdvise,
|
||||
@@ -136,6 +203,13 @@ impl Session {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Bulk variant of [`Session::add_item`]. Each tag address yields one
|
||||
/// `SubscribeResult` in the returned vector.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns [`Error::InvalidArgument`] when the input exceeds the
|
||||
/// gateway's 1000-item bulk cap, plus the usual command-level errors.
|
||||
pub async fn add_item_bulk(
|
||||
&self,
|
||||
server_handle: i32,
|
||||
@@ -155,6 +229,11 @@ impl Session {
|
||||
Ok(bulk_results(reply, BulkReplyKind::AddItemBulk))
|
||||
}
|
||||
|
||||
/// Bulk variant of [`Session::advise`].
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Same conditions as [`Session::add_item_bulk`].
|
||||
pub async fn advise_item_bulk(
|
||||
&self,
|
||||
server_handle: i32,
|
||||
@@ -174,6 +253,11 @@ impl Session {
|
||||
Ok(bulk_results(reply, BulkReplyKind::AdviseItemBulk))
|
||||
}
|
||||
|
||||
/// Bulk variant of [`Session::remove_item`].
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Same conditions as [`Session::add_item_bulk`].
|
||||
pub async fn remove_item_bulk(
|
||||
&self,
|
||||
server_handle: i32,
|
||||
@@ -193,6 +277,11 @@ impl Session {
|
||||
Ok(bulk_results(reply, BulkReplyKind::RemoveItemBulk))
|
||||
}
|
||||
|
||||
/// Bulk variant of [`Session::un_advise`].
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Same conditions as [`Session::add_item_bulk`].
|
||||
pub async fn un_advise_item_bulk(
|
||||
&self,
|
||||
server_handle: i32,
|
||||
@@ -212,6 +301,11 @@ impl Session {
|
||||
Ok(bulk_results(reply, BulkReplyKind::UnAdviseItemBulk))
|
||||
}
|
||||
|
||||
/// Bulk `Subscribe` (atomic add-and-advise) for a list of tag addresses.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Same conditions as [`Session::add_item_bulk`].
|
||||
pub async fn subscribe_bulk(
|
||||
&self,
|
||||
server_handle: i32,
|
||||
@@ -231,6 +325,12 @@ impl Session {
|
||||
Ok(bulk_results(reply, BulkReplyKind::SubscribeBulk))
|
||||
}
|
||||
|
||||
/// Bulk `Unsubscribe` (atomic un-advise-and-remove) for a list of
|
||||
/// item handles.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Same conditions as [`Session::add_item_bulk`].
|
||||
pub async fn unsubscribe_bulk(
|
||||
&self,
|
||||
server_handle: i32,
|
||||
@@ -250,6 +350,12 @@ impl Session {
|
||||
Ok(bulk_results(reply, BulkReplyKind::UnsubscribeBulk))
|
||||
}
|
||||
|
||||
/// Run MXAccess `Write` (single-value, no caller-supplied timestamp).
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns [`Error::Command`] for non-OK worker statuses, plus the
|
||||
/// usual transport/status errors.
|
||||
pub async fn write(
|
||||
&self,
|
||||
server_handle: i32,
|
||||
@@ -270,6 +376,11 @@ impl Session {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Run MXAccess `Write2` (single-value with caller-supplied timestamp).
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Same conditions as [`Session::write`].
|
||||
pub async fn write2(
|
||||
&self,
|
||||
server_handle: i32,
|
||||
@@ -292,10 +403,23 @@ impl Session {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Open the per-session event stream from the beginning.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns the `tonic::Status` mapped through [`Error::from`] when the
|
||||
/// gateway rejects the subscription.
|
||||
pub async fn events(&self) -> Result<EventStream, Error> {
|
||||
self.events_after(0).await
|
||||
}
|
||||
|
||||
/// Open the per-session event stream, requesting only events whose
|
||||
/// `worker_sequence` is greater than `after_worker_sequence`. Pass `0`
|
||||
/// to receive every buffered event.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Same conditions as [`Session::events`].
|
||||
pub async fn events_after(&self, after_worker_sequence: u64) -> Result<EventStream, Error> {
|
||||
self.client
|
||||
.stream_events(StreamEventsRequest {
|
||||
@@ -305,6 +429,13 @@ impl Session {
|
||||
.await
|
||||
}
|
||||
|
||||
/// Issue a raw `Invoke` for an arbitrary command, without filtering on
|
||||
/// the protocol status. Useful when callers need the full reply for
|
||||
/// commands not yet wrapped by `Session`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns the `tonic::Status` mapped through [`Error::from`].
|
||||
pub async fn invoke_raw(
|
||||
&self,
|
||||
kind: MxCommandKind,
|
||||
@@ -315,6 +446,13 @@ impl Session {
|
||||
.await
|
||||
}
|
||||
|
||||
/// Issue an `Invoke` for an arbitrary command and surface a non-OK
|
||||
/// reply as [`Error::Command`].
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns [`Error::Command`] for non-OK worker statuses plus any
|
||||
/// errors propagated by [`invoke_raw`](Self::invoke_raw).
|
||||
pub async fn invoke(
|
||||
&self,
|
||||
kind: MxCommandKind,
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
//! Rust-shaped wrappers around the wire `MxValue`, `MxArray`, and
|
||||
//! `MxStatusProxy` types.
|
||||
//!
|
||||
//! [`MxValue`] keeps both the original protobuf message and a friendly
|
||||
//! [`MxValueProjection`] enum, so callers can pass values through the wire
|
||||
//! without losing information while still being able to pattern-match on
|
||||
//! the typed variant. The same split applies to [`MxArrayValue`] and
|
||||
//! [`MxArrayProjection`]. [`MxStatus`] wraps the MXAccess `MxStatusProxy`
|
||||
//! status envelope.
|
||||
|
||||
use crate::generated::mxaccess_gateway::v1::mx_array::Values;
|
||||
use crate::generated::mxaccess_gateway::v1::mx_value::Kind;
|
||||
use crate::generated::mxaccess_gateway::v1::{
|
||||
@@ -6,6 +16,12 @@ use crate::generated::mxaccess_gateway::v1::{
|
||||
StringArray, TimestampArray,
|
||||
};
|
||||
|
||||
/// Owned `MxValue` carrying both the raw protobuf message and a typed
|
||||
/// [`MxValueProjection`] view.
|
||||
///
|
||||
/// The constructors set both `data_type` and `variant_type` to the values
|
||||
/// the worker expects so values built locally round-trip through MXAccess
|
||||
/// without surprises.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct MxValue {
|
||||
raw: ProtoMxValue,
|
||||
@@ -13,11 +29,14 @@ pub struct MxValue {
|
||||
}
|
||||
|
||||
impl MxValue {
|
||||
/// Wrap a protobuf [`ProtoMxValue`] and compute its
|
||||
/// [`MxValueProjection`].
|
||||
pub fn from_proto(raw: ProtoMxValue) -> Self {
|
||||
let projection = MxValueProjection::from_proto(&raw);
|
||||
Self { raw, projection }
|
||||
}
|
||||
|
||||
/// Build a boolean `MxValue` (`MxDataType::Boolean`, `VT_BOOL`).
|
||||
pub fn bool(value: bool) -> Self {
|
||||
Self::from_proto(ProtoMxValue {
|
||||
data_type: MxDataType::Boolean as i32,
|
||||
@@ -27,6 +46,7 @@ impl MxValue {
|
||||
})
|
||||
}
|
||||
|
||||
/// Build a 32-bit integer `MxValue` (`MxDataType::Integer`, `VT_I4`).
|
||||
pub fn int32(value: i32) -> Self {
|
||||
Self::from_proto(ProtoMxValue {
|
||||
data_type: MxDataType::Integer as i32,
|
||||
@@ -36,6 +56,7 @@ impl MxValue {
|
||||
})
|
||||
}
|
||||
|
||||
/// Build a 64-bit integer `MxValue` (`MxDataType::Integer`, `VT_I8`).
|
||||
pub fn int64(value: i64) -> Self {
|
||||
Self::from_proto(ProtoMxValue {
|
||||
data_type: MxDataType::Integer as i32,
|
||||
@@ -45,6 +66,7 @@ impl MxValue {
|
||||
})
|
||||
}
|
||||
|
||||
/// Build a 32-bit float `MxValue` (`MxDataType::Float`, `VT_R4`).
|
||||
pub fn float(value: f32) -> Self {
|
||||
Self::from_proto(ProtoMxValue {
|
||||
data_type: MxDataType::Float as i32,
|
||||
@@ -54,6 +76,7 @@ impl MxValue {
|
||||
})
|
||||
}
|
||||
|
||||
/// Build a 64-bit float `MxValue` (`MxDataType::Double`, `VT_R8`).
|
||||
pub fn double(value: f64) -> Self {
|
||||
Self::from_proto(ProtoMxValue {
|
||||
data_type: MxDataType::Double as i32,
|
||||
@@ -63,6 +86,7 @@ impl MxValue {
|
||||
})
|
||||
}
|
||||
|
||||
/// Build a string `MxValue` (`MxDataType::String`, `VT_BSTR`).
|
||||
pub fn string(value: impl Into<String>) -> Self {
|
||||
Self::from_proto(ProtoMxValue {
|
||||
data_type: MxDataType::String as i32,
|
||||
@@ -72,14 +96,18 @@ impl MxValue {
|
||||
})
|
||||
}
|
||||
|
||||
/// Borrow the underlying protobuf message exactly as it will travel
|
||||
/// over the wire.
|
||||
pub fn raw(&self) -> &ProtoMxValue {
|
||||
&self.raw
|
||||
}
|
||||
|
||||
/// Borrow the typed projection.
|
||||
pub fn projection(&self) -> &MxValueProjection {
|
||||
&self.projection
|
||||
}
|
||||
|
||||
/// Consume the wrapper and return the underlying protobuf message.
|
||||
pub fn into_proto(self) -> ProtoMxValue {
|
||||
self.raw
|
||||
}
|
||||
@@ -97,18 +125,35 @@ impl From<ProtoMxValue> for MxValue {
|
||||
}
|
||||
}
|
||||
|
||||
/// Typed view over an [`MxValue`].
|
||||
///
|
||||
/// Mirrors the `MxValue::Kind` oneof on the wire, plus a [`MxValueProjection::Null`]
|
||||
/// variant for `is_null=true` and a [`MxValueProjection::Unset`] variant for
|
||||
/// values that arrive without a `kind` set.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum MxValueProjection {
|
||||
/// No `kind` was present on the wire.
|
||||
Unset,
|
||||
/// `is_null = true` on the wire.
|
||||
Null,
|
||||
/// Boolean value.
|
||||
Bool(bool),
|
||||
/// 32-bit signed integer value.
|
||||
Int32(i32),
|
||||
/// 64-bit signed integer value.
|
||||
Int64(i64),
|
||||
/// 32-bit float value.
|
||||
Float(f32),
|
||||
/// 64-bit float value.
|
||||
Double(f64),
|
||||
/// UTF-8 string value.
|
||||
String(String),
|
||||
/// Wall-clock timestamp.
|
||||
Timestamp(prost_types::Timestamp),
|
||||
/// Array value carrying a homogeneous element type.
|
||||
Array(MxArrayValue),
|
||||
/// Opaque variant payload that the gateway could not project to a typed
|
||||
/// scalar.
|
||||
Raw(Vec<u8>),
|
||||
}
|
||||
|
||||
@@ -133,6 +178,8 @@ impl MxValueProjection {
|
||||
}
|
||||
}
|
||||
|
||||
/// Owned `MxArray` carrying both the raw protobuf message and a typed
|
||||
/// [`MxArrayProjection`] view of its elements.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct MxArrayValue {
|
||||
raw: MxArray,
|
||||
@@ -140,11 +187,14 @@ pub struct MxArrayValue {
|
||||
}
|
||||
|
||||
impl MxArrayValue {
|
||||
/// Wrap a protobuf [`MxArray`] and compute its
|
||||
/// [`MxArrayProjection`].
|
||||
pub fn from_proto(raw: MxArray) -> Self {
|
||||
let projection = MxArrayProjection::from_proto(&raw);
|
||||
Self { raw, projection }
|
||||
}
|
||||
|
||||
/// Build a one-dimensional string array (`VT_ARRAY|VT_BSTR`).
|
||||
pub fn string(values: Vec<String>) -> Self {
|
||||
Self::from_proto(MxArray {
|
||||
element_data_type: MxDataType::String as i32,
|
||||
@@ -155,25 +205,40 @@ impl MxArrayValue {
|
||||
})
|
||||
}
|
||||
|
||||
/// Borrow the underlying protobuf array message.
|
||||
pub fn raw(&self) -> &MxArray {
|
||||
&self.raw
|
||||
}
|
||||
|
||||
/// Borrow the typed projection of the array's elements.
|
||||
pub fn projection(&self) -> &MxArrayProjection {
|
||||
&self.projection
|
||||
}
|
||||
}
|
||||
|
||||
/// Typed view over an [`MxArrayValue`].
|
||||
///
|
||||
/// Each variant matches the corresponding `MxArray::Values` oneof arm on
|
||||
/// the wire.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum MxArrayProjection {
|
||||
/// No `values` oneof was present on the wire.
|
||||
Unset,
|
||||
/// Boolean elements.
|
||||
Bool(Vec<bool>),
|
||||
/// 32-bit signed integer elements.
|
||||
Int32(Vec<i32>),
|
||||
/// 64-bit signed integer elements.
|
||||
Int64(Vec<i64>),
|
||||
/// 32-bit float elements.
|
||||
Float(Vec<f32>),
|
||||
/// 64-bit float elements.
|
||||
Double(Vec<f64>),
|
||||
/// UTF-8 string elements.
|
||||
String(Vec<String>),
|
||||
/// Timestamp elements.
|
||||
Timestamp(Vec<prost_types::Timestamp>),
|
||||
/// Opaque variant payloads, one per element.
|
||||
Raw(Vec<Vec<u8>>),
|
||||
}
|
||||
|
||||
@@ -195,44 +260,57 @@ impl MxArrayProjection {
|
||||
}
|
||||
}
|
||||
|
||||
/// Typed wrapper around the MXAccess `MxStatusProxy` envelope, the
|
||||
/// per-value status that accompanies every `OnDataChange`/`Read` reply.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct MxStatus {
|
||||
raw: MxStatusProxy,
|
||||
}
|
||||
|
||||
impl MxStatus {
|
||||
/// Wrap a protobuf [`MxStatusProxy`].
|
||||
pub fn from_proto(raw: MxStatusProxy) -> Self {
|
||||
Self { raw }
|
||||
}
|
||||
|
||||
/// Borrow the underlying protobuf message.
|
||||
pub fn raw(&self) -> &MxStatusProxy {
|
||||
&self.raw
|
||||
}
|
||||
|
||||
/// `MXSTATUS_PROXY.Success` flag (0 = error, non-zero = good/warning).
|
||||
pub fn success(&self) -> i32 {
|
||||
self.raw.success
|
||||
}
|
||||
|
||||
/// Decode the status category, or `None` if the wire value is not a
|
||||
/// known [`MxStatusCategory`].
|
||||
pub fn category(&self) -> Option<MxStatusCategory> {
|
||||
MxStatusCategory::try_from(self.raw.category).ok()
|
||||
}
|
||||
|
||||
/// Decode the source (provider, gateway, worker) that flagged the
|
||||
/// status, or `None` for unknown values.
|
||||
pub fn detected_by(&self) -> Option<MxStatusSource> {
|
||||
MxStatusSource::try_from(self.raw.detected_by).ok()
|
||||
}
|
||||
|
||||
/// `MXSTATUS_PROXY.Detail` numeric reason code.
|
||||
pub fn detail(&self) -> i32 {
|
||||
self.raw.detail
|
||||
}
|
||||
|
||||
/// Raw, undecoded category integer (useful for forward compatibility).
|
||||
pub fn raw_category(&self) -> i32 {
|
||||
self.raw.raw_category
|
||||
}
|
||||
|
||||
/// Raw, undecoded detected-by integer.
|
||||
pub fn raw_detected_by(&self) -> i32 {
|
||||
self.raw.raw_detected_by
|
||||
}
|
||||
|
||||
/// Optional human-readable diagnostic from MXAccess.
|
||||
pub fn diagnostic_text(&self) -> &str {
|
||||
&self.raw.diagnostic_text
|
||||
}
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
//! Build-time version constants advertised by the Rust client.
|
||||
//!
|
||||
//! The protocol versions track the values the gateway and worker negotiate on
|
||||
//! `OpenSession` and let test harnesses cross-check the wire contract.
|
||||
|
||||
/// Semantic version of this Rust client crate. Mirrors `Cargo.toml`.
|
||||
pub const CLIENT_VERSION: &str = "0.1.0-dev";
|
||||
|
||||
/// Public gateway gRPC protocol version this client targets.
|
||||
pub const GATEWAY_PROTOCOL_VERSION: u32 = 1;
|
||||
|
||||
/// Internal worker IPC protocol version this client expects sessions to use.
|
||||
pub const WORKER_PROTOCOL_VERSION: u32 = 1;
|
||||
|
||||
Reference in New Issue
Block a user