# Rust Client Detailed Design ## Purpose Provide an async Rust client crate for MXAccess Gateway, plus a test CLI and unit tests. The Rust client should use `tonic` and `tokio`. Follow the [Rust Style Guide](./style-guides/RustStyleGuide.md) for handwritten code and the [Protobuf Style Guide](./style-guides/ProtobufStyleGuide.md) for generated contract inputs. ## Crate Layout Recommended layout: ```text clients/rust/ Cargo.toml build.rs crates/ mxgateway-client/ src/lib.rs src/client.rs src/session.rs src/options.rs src/auth.rs src/value.rs src/error.rs src/generated/ mxgw-cli/ src/main.rs tests/ ``` Expected dependencies: - `tonic` - `prost` - `prost-types` - `tokio` - `tokio-stream` - `thiserror` - `clap` - `serde` - `serde_json` - `tracing` ## Library API Suggested API: ```rust pub struct GatewayClient { /* tonic channel + generated client */ } pub struct ClientOptions { pub endpoint: String, pub api_key: String, pub plaintext: bool, pub ca_file: Option, pub server_name_override: Option, pub connect_timeout: Duration, pub call_timeout: Duration, } impl GatewayClient { pub async fn connect(options: ClientOptions) -> Result; pub async fn open_session(&self, options: OpenSessionOptions) -> Result; pub async fn invoke(&self, request: MxCommandRequest) -> Result; } ``` Session: ```rust pub struct Session { pub id: String, } impl Session { pub async fn register(&self, client_name: &str) -> Result; pub async fn add_item(&self, server_handle: i32, item: &str) -> Result; pub async fn add_item2(&self, server_handle: i32, item: &str, context: &str) -> Result; pub async fn advise(&self, server_handle: i32, item_handle: i32) -> Result<(), Error>; pub async fn write(&self, server_handle: i32, item_handle: i32, value: MxValue, user_id: i32) -> Result<(), Error>; pub async fn events(&self) -> Result>, Error>; pub async fn close(&self) -> Result<(), Error>; } ``` ## Authentication Use a `tonic` interceptor or request extension layer to add: ```text authorization: Bearer ``` Use `SecretString` or equivalent if a dependency is acceptable. Always redact API keys in `Debug` output. ## TLS Support: - plaintext channel for local development, - native or rustls TLS depending on project preference, - custom CA file, - domain override. ## Streaming Expose event streams as a `Stream>`. Dropping the stream should cancel the underlying gRPC stream. Do not buffer unboundedly in the client. If a helper channel is used, make it bounded. ## Error Handling Use `thiserror`: ```rust pub enum Error { Transport(tonic::transport::Error), Status(tonic::Status), Authentication(String), Authorization(String), Session(SessionError), Worker(WorkerError), Command(CommandError), MxAccess(MxAccessError), Timeout, Cancelled, } ``` Preserve raw command replies in `CommandError` where applicable. ## Test CLI Binary: `mxgw`. Use `clap` derive. Commands: ```text mxgw version mxgw smoke --endpoint http://localhost:5000 --api-key-env MXGATEWAY_API_KEY --item TestChildObject.TestInt mxgw stream-events --session-id --json mxgw write --session-id --server-handle 1 --item-handle 1 --type int32 --value 123 ``` JSON output should use `serde_json`. ## Unit Tests Use a fake `tonic` server started on a local ephemeral port, or abstract the generated client behind a trait for unit tests. Required tests: - generated client compiles from proto, - auth metadata injection, - TLS/plaintext endpoint construction, - value conversion, - command request construction, - error mapping from `tonic::Status`, - event stream order, - stream cancellation, - CLI parsing, - JSON redaction. ## Integration Tests Skip unless: ```text MXGATEWAY_INTEGRATION=1 ``` Use `tokio::test`. Run bounded smoke flow and ensure `CloseSession` is attempted with `drop` fallback docs, but do not rely on `Drop` for async close.