use std::fmt::Debug;
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum TransportBackend {
#[default]
Tungstenite,
Sockudo,
}
#[cfg_attr(
feature = "python",
pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.network", from_py_object)
)]
#[cfg_attr(
feature = "python",
pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.network")
)]
#[allow(
clippy::unsafe_derive_deserialize,
reason = "PyO3-backed config still needs serde deserialization for strict config decoding"
)]
#[derive(Clone, Debug, Serialize, Deserialize, bon::Builder)]
#[serde(deny_unknown_fields)]
pub struct WebSocketConfig {
pub url: String,
#[serde(default)]
#[builder(default)]
pub headers: Vec<(String, String)>,
#[serde(default)]
pub heartbeat: Option<u64>,
#[serde(default)]
pub heartbeat_msg: Option<String>,
#[serde(default)]
pub reconnect_timeout_ms: Option<u64>,
#[serde(default)]
pub reconnect_delay_initial_ms: Option<u64>,
#[serde(default)]
pub reconnect_delay_max_ms: Option<u64>,
#[serde(default)]
pub reconnect_backoff_factor: Option<f64>,
#[serde(default)]
pub reconnect_jitter_ms: Option<u64>,
#[serde(default)]
pub reconnect_max_attempts: Option<u32>,
#[serde(default)]
pub idle_timeout_ms: Option<u64>,
#[serde(default)]
#[builder(default)]
pub backend: TransportBackend,
#[serde(default)]
pub proxy_url: Option<String>,
}
#[cfg(test)]
mod tests {
use rstest::rstest;
use serde_json::json;
use super::WebSocketConfig;
#[rstest]
fn test_deserialize_websocket_config_rejects_unknown_field() {
let config = json!({
"url": "wss://example.com/ws",
"unexpected": true,
});
let error = serde_json::from_value::<WebSocketConfig>(config).unwrap_err();
assert!(error.to_string().contains("unknown field `unexpected`"));
}
}