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 {
38 if retry {
39 ConnectStrategy::Retry
40 } else {
41 ConnectStrategy::Fallback
42 }
43 }
44
45 pub fn is_fallback(&self) -> bool {
46 matches!(self, ConnectStrategy::Fallback)
47 }
48}
49
50impl TryFrom<JsValue> for ConnectStrategy {
51 type Error = Error;
52 fn try_from(value: JsValue) -> Result<Self> {
53 if value.is_undefined() || value.is_null() {
54 Ok(ConnectStrategy::default())
55 } else if let Some(string) = value.as_string() {
56 Ok(string.parse()?)
57 } else {
58 Ok(ConnectStrategy::try_from_js_value(value)?)
59 }
60 }
61}
62
63#[derive(Clone, Debug)]
68pub struct ConnectOptions {
69 pub block_async_connect: bool,
72 pub strategy: ConnectStrategy,
74 pub url: Option<String>,
77 pub connect_timeout: Option<Duration>,
81 pub retry_interval: Option<Duration>,
83}
84
85pub const DEFAULT_CONNECT_TIMEOUT_MILLIS: u64 = 5_000;
86pub const DEFAULT_CONNECT_RETRY_MILLIS: u64 = 5_000;
87
88impl Default for ConnectOptions {
89 fn default() -> Self {
90 Self {
91 block_async_connect: true,
92 strategy: ConnectStrategy::Retry,
93 url: None,
94 connect_timeout: None,
95 retry_interval: None,
96 }
97 }
98}
99
100impl ConnectOptions {
101 pub fn blocking_fallback() -> Self {
102 Self {
103 block_async_connect: true,
104 strategy: ConnectStrategy::Fallback,
105 url: None,
106 connect_timeout: None,
107 retry_interval: None,
108 }
109 }
110 pub fn blocking_retry() -> Self {
111 Self {
112 block_async_connect: true,
113 strategy: ConnectStrategy::Retry,
114 url: None,
115 connect_timeout: None,
116 retry_interval: None,
117 }
118 }
119
120 pub fn non_blocking_retry() -> Self {
121 Self {
122 block_async_connect: false,
123 strategy: ConnectStrategy::Retry,
124 url: None,
125 connect_timeout: None,
126 retry_interval: None,
127 }
128 }
129
130 pub fn with_url<S: Display>(self, url: S) -> Self {
131 Self {
132 url: Some(url.to_string()),
133 ..self
134 }
135 }
136
137 pub fn with_connect_timeout(self, timeout: Duration) -> Self {
138 Self {
139 connect_timeout: Some(timeout),
140 ..self
141 }
142 }
143
144 pub fn with_retry_interval(self, interval: Duration) -> Self {
145 Self {
146 retry_interval: Some(interval),
147 ..self
148 }
149 }
150
151 pub fn connect_timeout(&self) -> Duration {
152 self.connect_timeout
153 .unwrap_or(Duration::from_millis(DEFAULT_CONNECT_TIMEOUT_MILLIS))
154 }
155
156 pub fn retry_interval(&self) -> Duration {
157 self.retry_interval
158 .unwrap_or(Duration::from_millis(DEFAULT_CONNECT_RETRY_MILLIS))
159 }
160}
161
162cfg_if! {
163 if #[cfg(feature = "wasm32-sdk")] {
164 use js_sys::Object;
165 use wasm_bindgen::JsCast;
166 use workflow_wasm::extensions::object::*;
167
168 #[wasm_bindgen(typescript_custom_section)]
169 const TS_CONNECT_OPTIONS: &'static str = r#"
170
171 /**
172 * `ConnectOptions` is used to configure the `WebSocket` connectivity behavior.
173 *
174 * @category WebSocket
175 */
176 export interface IConnectOptions {
177 /**
178 * Indicates if the `async fn connect()` method should return immediately
179 * or wait for connection to occur or fail before returning.
180 * (default is `true`)
181 */
182 blockAsyncConnect? : boolean,
183 /**
184 * ConnectStrategy used to configure the retry or fallback behavior.
185 * In retry mode, the WebSocket will continuously attempt to connect to the server.
186 * (default is {link ConnectStrategy.Retry}).
187 */
188 strategy?: ConnectStrategy | string,
189 /**
190 * A custom URL that will change the current URL of the WebSocket.
191 * If supplied, the URL will override the use of resolver.
192 */
193 url?: string,
194 /**
195 * A custom connection timeout in milliseconds.
196 */
197 timeoutDuration?: number,
198 /**
199 * A custom retry interval in milliseconds.
200 */
201 retryInterval?: number,
202 }
203 "#;
204
205 #[wasm_bindgen]
206 extern "C" {
207 #[wasm_bindgen(typescript_type = "IConnectOptions | undefined")]
208 pub type IConnectOptions;
209 }
210
211 impl TryFrom<IConnectOptions> for ConnectOptions {
212 type Error = Error;
213 fn try_from(args: IConnectOptions) -> Result<Self> {
214 Self::try_from(&args)
215 }
216 }
217
218 impl TryFrom<&IConnectOptions> for ConnectOptions {
219 type Error = Error;
220 fn try_from(args: &IConnectOptions) -> Result<Self> {
221 let options = if let Some(args) = args.dyn_ref::<Object>() {
222 let url = args.get_value("url")?.as_string();
223 let block_async_connect = args
224 .get_value("blockAsyncConnect")?
225 .as_bool()
226 .unwrap_or(true);
227 let strategy = ConnectStrategy::try_from(args.get_value("strategy")?)?;
228 let timeout = args
229 .get_value("timeoutDuration")?
230 .as_f64()
231 .map(|f| Duration::from_millis(f as u64));
232 let retry_interval = args
233 .get_value("retryInterval")?
234 .as_f64()
235 .map(|f| Duration::from_millis(f as u64));
236
237 ConnectOptions {
238 block_async_connect,
239 strategy,
240 url,
241 connect_timeout: timeout,
242 retry_interval,
243 ..Default::default()
244 }
245 } else if let Some(retry) = args.as_bool() {
246 ConnectOptions {
247 block_async_connect: true,
248 strategy: ConnectStrategy::new(retry),
249 url: None,
250 connect_timeout: None,
251 retry_interval: None,
252 ..Default::default()
253 }
254 } else {
255 ConnectOptions::default()
256 };
257
258 Ok(options)
259 }
260 }
261 }
262}