use std::time::Duration;
pub const BINDING_JSONRPC: &str = "JSONRPC";
pub const BINDING_HTTP_JSON: &str = "HTTP+JSON";
pub const BINDING_REST: &str = "REST";
pub const BINDING_GRPC: &str = "GRPC";
#[derive(Debug, Clone)]
pub enum TlsConfig {
Disabled,
#[cfg(feature = "tls-rustls")]
Rustls,
}
#[allow(clippy::derivable_impls)]
impl Default for TlsConfig {
fn default() -> Self {
#[cfg(feature = "tls-rustls")]
{
Self::Rustls
}
#[cfg(not(feature = "tls-rustls"))]
{
Self::Disabled
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[non_exhaustive]
pub enum GrpcBareAddressScheme {
#[default]
HttpsExceptLoopback,
Https,
Http,
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct ClientConfig {
pub preferred_bindings: Vec<String>,
pub accepted_output_modes: Vec<String>,
pub history_length: Option<u32>,
pub return_immediately: bool,
pub request_timeout: Duration,
pub stream_connect_timeout: Duration,
pub connection_timeout: Duration,
pub max_response_size: usize,
pub tls: TlsConfig,
pub tenant: Option<String>,
}
impl ClientConfig {
#[must_use]
pub fn default_http() -> Self {
Self {
preferred_bindings: vec![BINDING_JSONRPC.into()],
accepted_output_modes: vec!["text/plain".into(), "application/json".into()],
history_length: None,
return_immediately: false,
request_timeout: Duration::from_secs(30),
stream_connect_timeout: Duration::from_secs(30),
connection_timeout: Duration::from_secs(10),
max_response_size: crate::transport::DEFAULT_MAX_RESPONSE_SIZE,
tls: TlsConfig::Disabled,
tenant: None,
}
}
}
impl Default for ClientConfig {
fn default() -> Self {
Self {
preferred_bindings: vec![BINDING_JSONRPC.into()],
accepted_output_modes: vec!["text/plain".into(), "application/json".into()],
history_length: None,
return_immediately: false,
request_timeout: Duration::from_secs(30),
stream_connect_timeout: Duration::from_secs(30),
connection_timeout: Duration::from_secs(10),
max_response_size: crate::transport::DEFAULT_MAX_RESPONSE_SIZE,
tls: TlsConfig::default(),
tenant: None,
}
}
}
impl ClientConfig {
#[must_use]
pub fn with_preferred_bindings(mut self, bindings: Vec<String>) -> Self {
self.preferred_bindings = bindings;
self
}
#[must_use]
pub fn with_accepted_output_modes(mut self, modes: Vec<String>) -> Self {
self.accepted_output_modes = modes;
self
}
#[must_use]
pub const fn with_history_length(mut self, length: Option<u32>) -> Self {
self.history_length = length;
self
}
#[must_use]
pub const fn with_return_immediately(mut self, val: bool) -> Self {
self.return_immediately = val;
self
}
#[must_use]
pub const fn with_request_timeout(mut self, timeout: Duration) -> Self {
self.request_timeout = timeout;
self
}
#[must_use]
pub const fn with_stream_connect_timeout(mut self, timeout: Duration) -> Self {
self.stream_connect_timeout = timeout;
self
}
#[must_use]
pub const fn with_connection_timeout(mut self, timeout: Duration) -> Self {
self.connection_timeout = timeout;
self
}
#[must_use]
pub const fn with_max_response_size(mut self, max_bytes: usize) -> Self {
self.max_response_size = max_bytes;
self
}
#[must_use]
pub const fn with_tls(mut self, tls: TlsConfig) -> Self {
self.tls = tls;
self
}
#[must_use]
pub fn with_tenant(mut self, tenant: Option<String>) -> Self {
self.tenant = tenant;
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_config_has_jsonrpc_binding() {
let cfg = ClientConfig::default();
assert_eq!(cfg.preferred_bindings, vec![BINDING_JSONRPC]);
}
#[test]
fn default_config_timeout() {
let cfg = ClientConfig::default();
assert_eq!(cfg.request_timeout, Duration::from_secs(30));
}
#[test]
fn default_http_config_is_disabled_tls() {
let cfg = ClientConfig::default_http();
assert!(matches!(cfg.tls, TlsConfig::Disabled));
}
#[test]
fn default_http_config_field_values() {
let cfg = ClientConfig::default_http();
assert_eq!(cfg.preferred_bindings, vec![BINDING_JSONRPC]);
assert_eq!(
cfg.accepted_output_modes,
vec!["text/plain", "application/json"]
);
assert!(cfg.history_length.is_none());
assert!(!cfg.return_immediately);
assert_eq!(cfg.request_timeout, Duration::from_secs(30));
assert_eq!(cfg.stream_connect_timeout, Duration::from_secs(30));
assert_eq!(cfg.connection_timeout, Duration::from_secs(10));
}
#[test]
fn default_config_field_values() {
let cfg = ClientConfig::default();
assert_eq!(cfg.preferred_bindings, vec![BINDING_JSONRPC]);
assert_eq!(
cfg.accepted_output_modes,
vec!["text/plain", "application/json"]
);
assert!(cfg.history_length.is_none());
assert!(!cfg.return_immediately);
assert_eq!(cfg.request_timeout, Duration::from_secs(30));
assert_eq!(cfg.stream_connect_timeout, Duration::from_secs(30));
assert_eq!(cfg.connection_timeout, Duration::from_secs(10));
}
#[test]
fn binding_constants_values() {
assert_eq!(BINDING_JSONRPC, "JSONRPC");
assert_eq!(BINDING_HTTP_JSON, "HTTP+JSON");
assert_eq!(BINDING_REST, "REST");
assert_eq!(BINDING_GRPC, "GRPC");
}
#[test]
fn every_setter_sets_its_field() {
let cfg = ClientConfig::default()
.with_preferred_bindings(vec!["GRPC".to_owned()])
.with_accepted_output_modes(vec!["image/png".to_owned()])
.with_history_length(Some(7))
.with_return_immediately(true)
.with_request_timeout(Duration::from_secs(1))
.with_stream_connect_timeout(Duration::from_secs(2))
.with_connection_timeout(Duration::from_secs(3))
.with_max_response_size(4)
.with_tls(TlsConfig::Disabled)
.with_tenant(Some("acme".to_owned()));
assert_eq!(cfg.preferred_bindings, vec!["GRPC".to_owned()]);
assert_eq!(cfg.accepted_output_modes, vec!["image/png".to_owned()]);
assert_eq!(cfg.history_length, Some(7));
assert!(cfg.return_immediately);
assert_eq!(cfg.request_timeout, Duration::from_secs(1));
assert_eq!(cfg.stream_connect_timeout, Duration::from_secs(2));
assert_eq!(cfg.connection_timeout, Duration::from_secs(3));
assert_eq!(cfg.max_response_size, 4);
assert!(matches!(cfg.tls, TlsConfig::Disabled));
assert_eq!(cfg.tenant.as_deref(), Some("acme"));
}
}