workflow_websocket/client/
options.rs1use super::error::Error;
2use super::result::Result;
3use cfg_if::cfg_if;
4use std::fmt::Display;
5use std::str::FromStr;
6use wasm_bindgen::convert::TryFromJsValue;
7use wasm_bindgen::prelude::*;
8use workflow_core::time::Duration;
9
10#[wasm_bindgen]
14#[derive(Default, Clone, Copy, Debug, Eq, PartialEq)]
15pub enum ConnectStrategy {
16 #[default]
19 Retry,
20 Fallback,
23}
24
25impl FromStr for ConnectStrategy {
26 type Err = Error;
27 fn from_str(s: &str) -> Result<Self> {
28 match s {
29 "retry" => Ok(ConnectStrategy::Retry),
30 "fallback" => Ok(ConnectStrategy::Fallback),
31 _ => Err(Error::InvalidConnectStrategyArg(s.to_string())),
32 }
33 }
34}
35
36impl ConnectStrategy {
37 pub fn new(retry: bool) -> Self {
40 if retry {
41 ConnectStrategy::Retry
42 } else {
43 ConnectStrategy::Fallback
44 }
45 }
46
47 pub fn is_fallback(&self) -> bool {
49 matches!(self, ConnectStrategy::Fallback)
50 }
51}
52
53impl TryFrom<JsValue> for ConnectStrategy {
54 type Error = Error;
55 fn try_from(value: JsValue) -> Result<Self> {
56 if value.is_undefined() || value.is_null() {
57 Ok(ConnectStrategy::default())
58 } else if let Some(string) = value.as_string() {
59 Ok(string.parse()?)
60 } else {
61 Ok(ConnectStrategy::try_from_js_value(value)?)
62 }
63 }
64}
65
66#[derive(Clone, Debug)]
71pub struct ConnectOptions {
72 pub block_async_connect: bool,
75 pub strategy: ConnectStrategy,
77 pub url: Option<String>,
80 pub connect_timeout: Option<Duration>,
84 pub retry_interval: Option<Duration>,
86}
87
88pub const DEFAULT_CONNECT_TIMEOUT_MILLIS: u64 = 5_000;
90pub const DEFAULT_CONNECT_RETRY_MILLIS: u64 = 5_000;
92
93impl Default for ConnectOptions {
94 fn default() -> Self {
95 Self {
96 block_async_connect: true,
97 strategy: ConnectStrategy::Retry,
98 url: None,
99 connect_timeout: None,
100 retry_interval: None,
101 }
102 }
103}
104
105impl ConnectOptions {
106 pub fn blocking_fallback() -> Self {
109 Self {
110 block_async_connect: true,
111 strategy: ConnectStrategy::Fallback,
112 url: None,
113 connect_timeout: None,
114 retry_interval: None,
115 }
116 }
117 pub fn blocking_retry() -> Self {
120 Self {
121 block_async_connect: true,
122 strategy: ConnectStrategy::Retry,
123 url: None,
124 connect_timeout: None,
125 retry_interval: None,
126 }
127 }
128
129 pub fn non_blocking_retry() -> Self {
132 Self {
133 block_async_connect: false,
134 strategy: ConnectStrategy::Retry,
135 url: None,
136 connect_timeout: None,
137 retry_interval: None,
138 }
139 }
140
141 pub fn with_url<S: Display>(self, url: S) -> Self {
143 Self {
144 url: Some(url.to_string()),
145 ..self
146 }
147 }
148
149 pub fn with_connect_timeout(self, timeout: Duration) -> Self {
151 Self {
152 connect_timeout: Some(timeout),
153 ..self
154 }
155 }
156
157 pub fn with_retry_interval(self, interval: Duration) -> Self {
159 Self {
160 retry_interval: Some(interval),
161 ..self
162 }
163 }
164
165 pub fn connect_timeout(&self) -> Duration {
168 self.connect_timeout
169 .unwrap_or(Duration::from_millis(DEFAULT_CONNECT_TIMEOUT_MILLIS))
170 }
171
172 pub fn retry_interval(&self) -> Duration {
175 self.retry_interval
176 .unwrap_or(Duration::from_millis(DEFAULT_CONNECT_RETRY_MILLIS))
177 }
178}
179
180cfg_if! {
181 if #[cfg(feature = "wasm32-sdk")] {
182 use js_sys::Object;
183 use wasm_bindgen::JsCast;
184 use workflow_wasm::extensions::object::*;
185
186 #[wasm_bindgen(typescript_custom_section)]
187 const TS_CONNECT_OPTIONS: &'static str = r#"
188
189 /**
190 * `ConnectOptions` is used to configure the `WebSocket` connectivity behavior.
191 *
192 * @category WebSocket
193 */
194 export interface IConnectOptions {
195 /**
196 * Indicates if the `async fn connect()` method should return immediately
197 * or wait for connection to occur or fail before returning.
198 * (default is `true`)
199 */
200 blockAsyncConnect? : boolean,
201 /**
202 * ConnectStrategy used to configure the retry or fallback behavior.
203 * In retry mode, the WebSocket will continuously attempt to connect to the server.
204 * (default is {link ConnectStrategy.Retry}).
205 */
206 strategy?: ConnectStrategy | string,
207 /**
208 * A custom URL that will change the current URL of the WebSocket.
209 * If supplied, the URL will override the use of resolver.
210 */
211 url?: string,
212 /**
213 * A custom connection timeout in milliseconds.
214 */
215 timeoutDuration?: number,
216 /**
217 * A custom retry interval in milliseconds.
218 */
219 retryInterval?: number,
220 }
221 "#;
222
223 #[wasm_bindgen]
224 extern "C" {
225 #[wasm_bindgen(typescript_type = "IConnectOptions | undefined")]
226 pub type IConnectOptions;
227 }
228
229 impl TryFrom<IConnectOptions> for ConnectOptions {
230 type Error = Error;
231 fn try_from(args: IConnectOptions) -> Result<Self> {
232 Self::try_from(&args)
233 }
234 }
235
236 impl TryFrom<&IConnectOptions> for ConnectOptions {
237 type Error = Error;
238 fn try_from(args: &IConnectOptions) -> Result<Self> {
239 let options = if let Some(args) = args.dyn_ref::<Object>() {
240 let url = args.get_value("url")?.as_string();
241 let block_async_connect = args
242 .get_value("blockAsyncConnect")?
243 .as_bool()
244 .unwrap_or(true);
245 let strategy = ConnectStrategy::try_from(args.get_value("strategy")?)?;
246 let timeout = args
247 .get_value("timeoutDuration")?
248 .as_f64()
249 .map(|f| Duration::from_millis(f as u64));
250 let retry_interval = args
251 .get_value("retryInterval")?
252 .as_f64()
253 .map(|f| Duration::from_millis(f as u64));
254
255 ConnectOptions {
256 block_async_connect,
257 strategy,
258 url,
259 connect_timeout: timeout,
260 retry_interval,
261 ..Default::default()
262 }
263 } else if let Some(retry) = args.as_bool() {
264 ConnectOptions {
265 block_async_connect: true,
266 strategy: ConnectStrategy::new(retry),
267 url: None,
268 connect_timeout: None,
269 retry_interval: None,
270 ..Default::default()
271 }
272 } else {
273 ConnectOptions::default()
274 };
275
276 Ok(options)
277 }
278 }
279 }
280}