1#[cfg(feature = "serde")]
35use serde::{Deserialize, Serialize};
36
37#[cfg(feature = "typescript-bindings")]
38use ts_rs::TS;
39
40#[cfg(feature = "uniffi-bindings")]
41uniffi::setup_scaffolding!();
42
43use std::net::SocketAddr;
44#[cfg(feature = "uniffi-bindings")]
45use std::str::FromStr;
46#[cfg(feature = "uniffi-bindings")]
47uniffi::custom_type!(SocketAddr, String, {
48 remote,
49 try_lift: |val| Ok(SocketAddr::from_str(&val)?),
50 lower: |val| val.to_string()
51});
52
53#[derive(Debug, PartialEq, Clone)]
54#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
55#[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Record))]
56#[cfg_attr(
57 feature = "typescript-bindings",
58 derive(TS),
59 ts(export),
60 ts(export_to = "bindings.ts")
61)]
62#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
63pub struct PersistedClientConfig {
64 pub version: String,
65 pub transports: Vec<ClientConfig>,
66}
67
68impl PersistedClientConfig {
69 pub fn get_addrs(&self) -> Vec<SocketAddr> {
70 let mut addrs = Vec::new();
71 for transport in &self.transports {
72 match transport {
73 ClientConfig::QuicPlain(params) => addrs.extend(¶ms.addresses),
74 ClientConfig::TlsPlain(params) => addrs.extend(¶ms.addresses),
75 }
76 }
77 addrs
78 }
79}
80
81#[derive(Debug, PartialEq, Clone)]
82#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
83#[cfg_attr(feature = "serde", serde(tag = "transport_type", content = "args"))]
84#[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Enum))]
85#[cfg_attr(
86 feature = "typescript-bindings",
87 derive(TS),
88 ts(export),
89 ts(export_to = "bindings.ts")
90)]
91#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
92pub enum ClientConfig {
93 QuicPlain(quic::ClientOptions),
94 TlsPlain(tls::ClientOptions),
95}
96
97impl From<quic::ClientOptions> for ClientConfig {
98 fn from(value: quic::ClientOptions) -> Self {
99 ClientConfig::QuicPlain(value)
100 }
101}
102
103impl From<tls::ClientOptions> for ClientConfig {
104 fn from(value: tls::ClientOptions) -> Self {
105 ClientConfig::TlsPlain(value)
106 }
107}
108
109pub mod quic {
110 #[cfg(feature = "serde")]
111 use serde::{Deserialize, Serialize};
112 use std::net::SocketAddr;
113
114 #[cfg(feature = "typescript-bindings")]
115 use ts_rs::TS;
116
117 #[derive(Debug, PartialEq, Clone)]
118 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
119 #[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Record))]
120 #[cfg_attr(
121 feature = "typescript-bindings",
122 derive(TS),
123 ts(export),
124 ts(export_to = "bindings.ts")
125 )]
126 #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
127 pub struct QuicPlainClientOptions {
128 pub addresses: Vec<SocketAddr>,
134
135 pub host: Option<String>,
137
138 pub id_pubkey: String,
140 }
141
142 pub type ClientOptions = QuicPlainClientOptions;
143}
144
145pub mod tls {
146 #[cfg(feature = "serde")]
147 use serde::{Deserialize, Serialize};
148 use std::net::SocketAddr;
149
150 #[cfg(feature = "typescript-bindings")]
151 use ts_rs::TS;
152
153 #[derive(Debug, PartialEq, Clone)]
154 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
155 #[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Record))]
156 #[cfg_attr(
157 feature = "typescript-bindings",
158 derive(TS),
159 ts(export),
160 ts(export_to = "bindings.ts")
161 )]
162 #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
163 pub struct TlsPlainClientOptions {
164 pub addresses: Vec<SocketAddr>,
170
171 pub host: Option<String>,
173
174 pub id_pubkey: String,
176 }
177
178 pub type ClientOptions = TlsPlainClientOptions;
179}
180
181#[cfg(test)]
182mod test {
183 use crate::{ClientConfig, PersistedClientConfig};
184
185 const RAW_V0_CLIENT_CONFIG: &str = r#"{"version":"0","transports":[{"transport_type":"quic_plain","args":{"addresses":["139.162.33.226:4443","[2400:8901::2000:faff:fea6:87f2]:4443"],"host":"netdna.bootstrapcdn.com","id_pubkey":"9JC91ZiszhIn3n4FG+MDYE/lYwhGdpHGWQTKUqGl+sE="}}]}"#;
186
187 #[test]
192 fn ensure_bridge_v0_parsing_compatibility() -> Result<(), Box<dyn std::error::Error>> {
193 let parsed: PersistedClientConfig = serde_json::from_str(RAW_V0_CLIENT_CONFIG)?;
195
196 assert_eq!(parsed.version, "0");
198
199 let params = match &parsed.transports[0] {
201 ClientConfig::QuicPlain(p) => p,
202 ClientConfig::TlsPlain(_) => return Err("expected quic transport args".into()),
203 };
204
205 let addresses = ¶ms.addresses;
207
208 let address_strings: Vec<String> = addresses.iter().map(|v| v.to_string()).collect();
209
210 assert!(
212 address_strings
213 .iter()
214 .any(|addr| addr.contains("139.162.33.226:4443"))
215 );
216 assert!(
217 address_strings
218 .iter()
219 .any(|addr| addr.contains("[2400:8901::2000:faff:fea6:87f2]:4443"))
220 );
221
222 assert_eq!(params.host, Some("netdna.bootstrapcdn.com".to_string()),);
224
225 Ok(())
226 }
227}