simulator-client 0.7.1

Async WebSocket client for the Solana simulator backtest API
Documentation
use thiserror::Error;

/// Error converting a URL scheme.
#[derive(Debug, Error)]
pub enum UrlError {
    #[error("cannot derive WebSocket URL from `{url}`: expected http:// or https:// scheme")]
    InvalidScheme { url: String },
}

/// Convert an `http(s)://` RPC endpoint URL to a `ws(s)://` URL.
pub fn http_to_ws_url(url: &str) -> Result<String, UrlError> {
    let scheme = if url.starts_with("https://") {
        "wss"
    } else if url.starts_with("http://") {
        "ws"
    } else {
        return Err(UrlError::InvalidScheme {
            url: url.to_string(),
        });
    };
    let rest = url.split_once("://").map(|x| x.1).unwrap_or(url);
    Ok(format!("{scheme}://{rest}"))
}

/// Derive an HTTP base URL from a WebSocket URL.
/// `wss://host:port/path` → `https://host:port`
pub fn http_base_from_ws_url(ws_url: &str) -> String {
    let http = ws_url
        .replacen("wss://", "https://", 1)
        .replacen("ws://", "http://", 1);
    if let Some(start) = http.find("://").map(|i| i + 3)
        && let Some(slash) = http[start..].find('/')
    {
        return http[..start + slash].to_string();
    }
    http
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn http_to_ws_converts_http() {
        assert_eq!(
            http_to_ws_url("http://localhost:8899").unwrap(),
            "ws://localhost:8899"
        );
    }

    #[test]
    fn http_to_ws_converts_https() {
        assert_eq!(
            http_to_ws_url("https://api.mainnet-beta.solana.com").unwrap(),
            "wss://api.mainnet-beta.solana.com"
        );
    }

    #[test]
    fn http_to_ws_rejects_other_schemes() {
        assert!(matches!(
            http_to_ws_url("ws://example.com"),
            Err(UrlError::InvalidScheme { .. })
        ));
        assert!(matches!(
            http_to_ws_url("ftp://example.com"),
            Err(UrlError::InvalidScheme { .. })
        ));
        assert!(matches!(
            http_to_ws_url("example.com"),
            Err(UrlError::InvalidScheme { .. })
        ));
    }

    #[test]
    fn http_base_strips_path() {
        assert_eq!(
            http_base_from_ws_url("wss://host:8900/backtest"),
            "https://host:8900"
        );
        assert_eq!(
            http_base_from_ws_url("ws://localhost:8900/backtest"),
            "http://localhost:8900"
        );
    }

    #[test]
    fn http_base_no_path() {
        assert_eq!(
            http_base_from_ws_url("wss://host:8900"),
            "https://host:8900"
        );
    }
}