Skip to main content

karyon_jsonrpc/client/
builder.rs

1use std::sync::Arc;
2
3use karyon_core::async_runtime::Executor;
4use karyon_net::ToEndpoint;
5
6#[cfg(any(feature = "tcp", feature = "tls", feature = "quic"))]
7use karyon_net::Endpoint;
8
9#[cfg(feature = "quic")]
10use karyon_net::quic::ClientQuicConfig;
11#[cfg(feature = "tcp")]
12use karyon_net::tcp::TcpConfig;
13
14use crate::{
15    client::WsCodec,
16    codec::{JsonCodec, JsonRpcCodec},
17    error::Result,
18};
19
20#[cfg(any(feature = "tcp", feature = "tls", feature = "quic"))]
21use crate::error::Error;
22
23use super::{Client, ClientConfig};
24
25const DEFAULT_TIMEOUT: u64 = 3000;
26const DEFAULT_MAX_SUBSCRIPTION_BUFFER_SIZE: usize = 20000;
27
28/// Builder for constructing an RPC [`Client`].
29///
30/// # Example
31///
32/// ```no_run
33/// use karyon_jsonrpc::client::ClientBuilder;
34///
35/// async {
36///     let client = ClientBuilder::new("tcp://127.0.0.1:60000")
37///         .expect("create builder")
38///         .set_timeout(5000)
39///         .build().await
40///         .expect("build client");
41///
42///     let result: i32 = client
43///         .call("Calc.add", (1, 2))
44///         .await
45///         .expect("call method");
46/// };
47/// ```
48pub struct ClientBuilder<B, W = JsonCodec> {
49    inner: ClientConfig,
50    byte_codec: B,
51    ws_codec: W,
52    executor: Option<Executor>,
53}
54
55impl ClientBuilder<JsonCodec, JsonCodec> {
56    /// Create a builder with the default JSON codec.
57    pub fn new(endpoint: impl ToEndpoint) -> Result<ClientBuilder<JsonCodec, JsonCodec>> {
58        ClientBuilder::new_with_codec(endpoint, JsonCodec::default())
59    }
60}
61
62impl<B: JsonRpcCodec> ClientBuilder<B, JsonCodec> {
63    /// Create a builder with a custom byte-stream codec. The default
64    /// `JsonCodec` is used for `ws://` / `wss://` endpoints; override
65    /// with `with_ws_codec`.
66    pub fn new_with_codec(
67        endpoint: impl ToEndpoint,
68        codec: B,
69    ) -> Result<ClientBuilder<B, JsonCodec>> {
70        let endpoint = endpoint.to_endpoint()?;
71        Ok(ClientBuilder {
72            inner: ClientConfig {
73                endpoint,
74                timeout: Some(DEFAULT_TIMEOUT),
75                #[cfg(feature = "tcp")]
76                tcp_config: Default::default(),
77                #[cfg(feature = "tls")]
78                tls_config: None,
79                #[cfg(feature = "quic")]
80                quic_config: None,
81                subscription_buffer_size: DEFAULT_MAX_SUBSCRIPTION_BUFFER_SIZE,
82            },
83            byte_codec: codec,
84            ws_codec: JsonCodec::default(),
85            executor: None,
86        })
87    }
88}
89
90impl<B, W> ClientBuilder<B, W>
91where
92    B: JsonRpcCodec,
93    W: WsCodec,
94{
95    /// Set request timeout in milliseconds.
96    pub fn set_timeout(mut self, timeout: u64) -> Self {
97        self.inner.timeout = Some(timeout);
98        self
99    }
100
101    /// Set max subscription buffer size.
102    pub fn set_max_subscription_buffer_size(mut self, size: usize) -> Self {
103        self.inner.subscription_buffer_size = size;
104        self
105    }
106
107    /// Set TCP config.
108    #[cfg(feature = "tcp")]
109    pub fn tcp_config(mut self, config: TcpConfig) -> Result<Self> {
110        match self.inner.endpoint {
111            Endpoint::Tcp(..) | Endpoint::Tls(..) | Endpoint::Ws(..) | Endpoint::Wss(..) => {
112                self.inner.tcp_config = config;
113                Ok(self)
114            }
115            _ => Err(Error::UnsupportedProtocol(self.inner.endpoint.to_string())),
116        }
117    }
118
119    /// Set TLS config.
120    #[cfg(feature = "tls")]
121    pub fn tls_config(mut self, config: karyon_net::tls::ClientTlsConfig) -> Result<Self> {
122        match self.inner.endpoint {
123            Endpoint::Tls(..) | Endpoint::Wss(..) => {
124                self.inner.tls_config = Some(config);
125                Ok(self)
126            }
127            _ => Err(Error::UnsupportedProtocol(format!(
128                "Invalid tls config for endpoint: {}",
129                self.inner.endpoint
130            ))),
131        }
132    }
133
134    /// Set QUIC config.
135    #[cfg(feature = "quic")]
136    pub fn quic_config(mut self, config: ClientQuicConfig) -> Result<Self> {
137        match self.inner.endpoint {
138            Endpoint::Quic(..) => {
139                self.inner.quic_config = Some(config);
140                Ok(self)
141            }
142            #[cfg(feature = "http3")]
143            Endpoint::Http(..) => {
144                self.inner.quic_config = Some(config);
145                Ok(self)
146            }
147            _ => Err(Error::UnsupportedProtocol(format!(
148                "Invalid quic config for endpoint: {}",
149                self.inner.endpoint
150            ))),
151        }
152    }
153
154    /// Override the WebSocket codec. Only meaningful for `ws://` /
155    /// `wss://` endpoints.
156    #[cfg(feature = "ws")]
157    pub fn with_ws_codec<W2: WsCodec>(self, ws_codec: W2) -> ClientBuilder<B, W2> {
158        ClientBuilder {
159            inner: self.inner,
160            byte_codec: self.byte_codec,
161            ws_codec,
162            executor: self.executor,
163        }
164    }
165
166    /// Set an executor. Used for the client's task group.
167    pub fn with_executor(mut self, ex: Executor) -> Self {
168        self.executor = Some(ex);
169        self
170    }
171
172    /// Build the client.
173    pub async fn build(self) -> Result<Arc<Client<B, W>>> {
174        Client::init(self.inner, self.byte_codec, self.ws_codec, self.executor).await
175    }
176}