use std::sync::Arc;
use rvoip_sip_core::types::Method;
use crate::api::handle::CallId;
use crate::api::headers::{take_staged, BuilderHeaderState, SipRequestOptions};
use crate::api::unified::UnifiedCoordinator;
use crate::errors::{Result, SessionError};
pub struct GenericResponseBuilder {
coord: Arc<UnifiedCoordinator>,
call_id: CallId,
method: Method,
status: u16,
reason: Option<String>,
state: BuilderHeaderState,
}
impl GenericResponseBuilder {
pub(crate) fn new(
coord: Arc<UnifiedCoordinator>,
call_id: CallId,
method: Method,
status: u16,
) -> Result<Self> {
if !(300..=699).contains(&status) {
return Err(SessionError::InvalidInput(format!(
"GenericResponseBuilder status must be 3xx/4xx/5xx/6xx, got {status}"
)));
}
Ok(Self {
coord,
call_id,
method,
status,
reason: None,
state: BuilderHeaderState::default(),
})
}
pub fn with_reason(mut self, r: impl Into<String>) -> Self {
self.reason = Some(r.into());
self
}
pub async fn send(mut self) -> Result<()> {
let reason = self.reason.unwrap_or_else(|| "OK".to_string());
let extras = take_staged(&mut self.state);
if (300..=399).contains(&self.status) {
if extras.is_empty() {
self.coord
.helpers
.redirect_call(&self.call_id, self.status, vec![reason])
.await
} else {
self.coord
.dialog_adapter()
.send_redirect_response_with_options(
&self.call_id,
self.status,
vec![reason],
extras,
)
.await
}
} else if extras.is_empty() {
self.coord
.helpers
.reject_call(&self.call_id, self.status, &reason)
.await
} else {
self.coord
.dialog_adapter()
.send_response_with_options(&self.call_id, self.status, None, extras)
.await?;
self.coord
.helpers
.reject_call(&self.call_id, self.status, &reason)
.await
.or(Ok(()))
}
}
}
impl SipRequestOptions for GenericResponseBuilder {
fn method(&self) -> Method {
self.method.clone()
}
fn header_state_mut(&mut self) -> &mut BuilderHeaderState {
&mut self.state
}
fn header_state(&self) -> &BuilderHeaderState {
&self.state
}
}