1use thiserror::Error;
2
3#[derive(Debug, Error)]
5pub enum UrlError {
6 #[error("cannot derive WebSocket URL from `{url}`: expected http:// or https:// scheme")]
7 InvalidScheme { url: String },
8}
9
10pub fn http_to_ws_url(url: &str) -> Result<String, UrlError> {
12 let scheme = if url.starts_with("https://") {
13 "wss"
14 } else if url.starts_with("http://") {
15 "ws"
16 } else {
17 return Err(UrlError::InvalidScheme {
18 url: url.to_string(),
19 });
20 };
21 let rest = url.split_once("://").map(|x| x.1).unwrap_or(url);
22 Ok(format!("{scheme}://{rest}"))
23}
24
25pub(crate) fn http_base_from_ws_url(ws_url: &str) -> String {
28 let http = ws_url
29 .replacen("wss://", "https://", 1)
30 .replacen("ws://", "http://", 1);
31 if let Some(start) = http.find("://").map(|i| i + 3)
32 && let Some(slash) = http[start..].find('/')
33 {
34 return http[..start + slash].to_string();
35 }
36 http
37}
38
39#[cfg(test)]
40mod tests {
41 use super::*;
42
43 #[test]
44 fn http_to_ws_converts_http() {
45 assert_eq!(
46 http_to_ws_url("http://localhost:8899").unwrap(),
47 "ws://localhost:8899"
48 );
49 }
50
51 #[test]
52 fn http_to_ws_converts_https() {
53 assert_eq!(
54 http_to_ws_url("https://api.mainnet-beta.solana.com").unwrap(),
55 "wss://api.mainnet-beta.solana.com"
56 );
57 }
58
59 #[test]
60 fn http_to_ws_rejects_other_schemes() {
61 assert!(matches!(
62 http_to_ws_url("ws://example.com"),
63 Err(UrlError::InvalidScheme { .. })
64 ));
65 assert!(matches!(
66 http_to_ws_url("ftp://example.com"),
67 Err(UrlError::InvalidScheme { .. })
68 ));
69 assert!(matches!(
70 http_to_ws_url("example.com"),
71 Err(UrlError::InvalidScheme { .. })
72 ));
73 }
74
75 #[test]
76 fn http_base_strips_path() {
77 assert_eq!(
78 http_base_from_ws_url("wss://host:8900/backtest"),
79 "https://host:8900"
80 );
81 assert_eq!(
82 http_base_from_ws_url("ws://localhost:8900/backtest"),
83 "http://localhost:8900"
84 );
85 }
86
87 #[test]
88 fn http_base_no_path() {
89 assert_eq!(
90 http_base_from_ws_url("wss://host:8900"),
91 "https://host:8900"
92 );
93 }
94}