use http::header::{ACCEPT, CONTENT_TYPE};
use http::StatusCode;
use url::Url;
use crate::http::CONTENT_TYPE_FORM_URLENCODED;
use crate::standards::websub::{Result, SubscribeRequest, SubscriptionMode, WebSubError};
#[derive(Debug, Clone)]
pub struct SubscribeResponse {
pub status: StatusCode,
}
#[tracing::instrument(skip(client))]
pub async fn subscribe(
client: &impl crate::http::Client,
hub: &Url,
request: &SubscribeRequest,
) -> Result<SubscribeResponse> {
send_subscription_request(client, hub, request, SubscriptionMode::Subscribe).await
}
#[tracing::instrument(skip(client))]
pub async fn unsubscribe(
client: &impl crate::http::Client,
hub: &Url,
request: &SubscribeRequest,
) -> Result<SubscribeResponse> {
send_subscription_request(client, hub, request, SubscriptionMode::Unsubscribe).await
}
#[tracing::instrument(skip(client))]
pub async fn send_subscription_request(
client: &impl crate::http::Client,
hub: &Url,
request: &SubscribeRequest,
mode: SubscriptionMode,
) -> Result<SubscribeResponse> {
let mut pairs = vec![
("hub.mode".to_string(), match mode { SubscriptionMode::Subscribe => "subscribe", SubscriptionMode::Unsubscribe => "unsubscribe" }.to_string()),
("hub.topic".to_string(), request.topic.as_str().to_string()),
("hub.callback".to_string(), request.callback.as_str().to_string()),
];
if let Some(lease_seconds) = request.lease_seconds {
pairs.push(("hub.lease_seconds".to_string(), lease_seconds.to_string()));
}
if let Some(secret) = request.secret.as_ref() {
pairs.push(("hub.secret".to_string(), secret.to_string()));
}
let body = url::form_urlencoded::Serializer::new(String::new())
.extend_pairs(pairs)
.finish();
let request = http::Request::builder()
.method(http::Method::POST)
.uri(hub.as_str())
.header(ACCEPT, "*/*")
.header(CONTENT_TYPE, CONTENT_TYPE_FORM_URLENCODED)
.body(crate::http::Body::Bytes(body.into_bytes()))
.map_err(|err| WebSubError::Http(err.to_string()))?;
let response = client.send_request(request).await.map_err(|err| WebSubError::Network(Box::new(err)))?;
let status = response.status();
if status == http::StatusCode::ACCEPTED || status == http::StatusCode::NO_CONTENT || status == http::StatusCode::OK {
Ok(SubscribeResponse { status })
} else {
Err(WebSubError::Http(format!("Hub rejected request: {}", status)))
}
}