use tokio_tungstenite::tungstenite::{
client::IntoClientRequest,
http::{Request, header::AUTHORIZATION},
};
use url::Url;
use crate::types::errors::ClientError;
#[derive(Clone, Debug)]
pub struct ConnectionOptions {
pub host: String,
pub port: i64,
pub tls: bool,
pub pat_token: Option<String>,
}
impl ConnectionOptions {
pub fn new(host: impl Into<String>, port: i64, tls: bool, pat_token: Option<String>) -> Self {
Self {
host: host.into(),
port,
tls,
pat_token,
}
}
pub fn ws_url(&self) -> Result<String, ClientError> {
build_ws_url(&self.host, self.port, self.tls)
}
}
pub(crate) fn build_ws_request(options: &ConnectionOptions) -> Result<Request<()>, ClientError> {
let url = options.ws_url()?;
let mut request = url
.into_client_request()
.map_err(|_| ClientError::InvalidInput("invalid host/port"))?;
if let Some(token) = options.pat_token.as_deref() {
let value = format!("Bearer {token}");
let header_value = value
.parse()
.map_err(|_| ClientError::InvalidInput("invalid PAT token format"))?;
request.headers_mut().insert(AUTHORIZATION, header_value);
}
Ok(request)
}
fn build_ws_url(host: &str, port: i64, tls: bool) -> Result<String, ClientError> {
let scheme = if tls { "wss" } else { "ws" };
let base = format!("{scheme}://{host}:{port}/ws");
let url = Url::parse(&base).map_err(|_| ClientError::InvalidInput("invalid host/port"))?;
Ok(url.into())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn request_includes_authorization_header_when_pat_is_set() {
let options =
ConnectionOptions::new("127.0.0.1", 8245, false, Some("pat_test.token".to_string()));
let request = build_ws_request(&options).expect("request should build");
let auth = request
.headers()
.get(AUTHORIZATION)
.expect("authorization header missing")
.to_str()
.expect("header should be valid utf8");
assert_eq!(auth, "Bearer pat_test.token");
}
#[test]
fn request_omits_authorization_header_when_pat_is_not_set() {
let options = ConnectionOptions::new("127.0.0.1", 8245, false, None);
let request = build_ws_request(&options).expect("request should build");
assert!(request.headers().get(AUTHORIZATION).is_none());
}
}