Skip to main content

trillium_grpc/client/
service_client.rs

1//! Extension surface for generated `<Service>Client` newtypes.
2//!
3//! Codegen emits `impl ServiceClient for <Service>Client` for every service
4//! it produces. The blanket [`ServiceClientExt`] then provides the
5//! builder/setter methods every service client gets for free.
6//!
7//! New options live on [`ServiceClientExt`] — adding one is a single
8//! method here, no codegen change.
9
10use crate::{Encoding, timeout::format_grpc_timeout};
11use std::time::Duration;
12
13/// Generated `<Service>Client` newtypes implement this so extension traits
14/// can configure the underlying [`trillium_client::Client`].
15pub trait ServiceClient {
16    /// The underlying connection client.
17    fn client(&self) -> &trillium_client::Client;
18    /// The underlying connection client, mutably — the hook the
19    /// [`ServiceClientExt`] setters write through.
20    fn client_mut(&mut self) -> &mut trillium_client::Client;
21}
22
23/// Builder-style configuration available on every service client.
24/// Implemented for any `T: ServiceClient + Sized`, so service clients
25/// don't reimplement these — they just `impl ServiceClient` and inherit
26/// the full set.
27pub trait ServiceClientExt: ServiceClient + Sized {
28    /// Compress every outgoing request body with this codec. Sent on the
29    /// wire as `grpc-encoding: <codec>`.
30    ///
31    /// The server is required by spec to handle whatever the client sends
32    /// — including failing with `Unimplemented` — so picking a codec your
33    /// server is known to support is the caller's responsibility. The
34    /// server's `grpc-accept-encoding` (visible after the first response)
35    /// can be inspected by the caller to pick a future-safe codec.
36    ///
37    /// Setting `Encoding::Identity` clears any previously-set compression.
38    fn with_outbound_compression(mut self, encoding: Encoding) -> Self {
39        self.set_outbound_compression(encoding);
40        self
41    }
42
43    /// `&mut` form of [`with_outbound_compression`](Self::with_outbound_compression).
44    fn set_outbound_compression(&mut self, encoding: Encoding) -> &mut Self {
45        let headers = self.client_mut().default_headers_mut();
46        if matches!(encoding, Encoding::Identity) {
47            headers.remove("grpc-encoding");
48        } else {
49            headers.insert("grpc-encoding", encoding.as_grpc_encoding());
50        }
51        self
52    }
53
54    /// The currently-configured outbound compression. `Identity` if none
55    /// has been set.
56    fn outbound_compression(&self) -> Encoding {
57        self.client()
58            .default_headers()
59            .get_str("grpc-encoding")
60            .and_then(Encoding::from_grpc_encoding)
61            .unwrap_or(Encoding::Identity)
62    }
63
64    /// Apply this duration as the default deadline on every call. Sent on
65    /// the wire as `grpc-timeout: <unit>` so the server can enforce the
66    /// same deadline; the client also races its own dispatch future
67    /// against a local timer so we fail fast even if the server is unable
68    /// to respond.
69    ///
70    /// Setting `Duration::ZERO` clears any previously-set deadline.
71    fn with_default_timeout(mut self, duration: Duration) -> Self {
72        self.set_default_timeout(duration);
73        self
74    }
75
76    /// `&mut` form of [`with_default_timeout`](Self::with_default_timeout).
77    fn set_default_timeout(&mut self, duration: Duration) -> &mut Self {
78        let headers = self.client_mut().default_headers_mut();
79        if duration.is_zero() {
80            headers.remove("grpc-timeout");
81        } else {
82            headers.insert("grpc-timeout", format_grpc_timeout(duration));
83        }
84        self
85    }
86
87    /// The currently-configured default timeout, if any.
88    fn default_timeout(&self) -> Option<Duration> {
89        self.client()
90            .default_headers()
91            .get_str("grpc-timeout")
92            .and_then(crate::timeout::parse_grpc_timeout)
93    }
94}
95
96impl<T: ServiceClient> ServiceClientExt for T {}