use std::sync::Arc;
use rvoip_sip_core::types::Method;
use crate::api::handle::CallId;
use crate::api::headers::{BuilderHeaderState, SipRequestOptions};
use crate::api::unified::UnifiedCoordinator;
use crate::errors::Result;
use crate::types::Credentials;
#[non_exhaustive]
#[derive(Default, Debug, Clone)]
pub enum PaiOverride {
#[default]
Default,
Suppress,
Use(String),
}
#[non_exhaustive]
#[derive(Default, Debug, Clone)]
pub enum ProxyOverride {
#[default]
Default,
Suppress,
Use(String),
}
#[derive(Default, Debug, Clone)]
pub struct OutboundCallOptionsSnapshot {
pub from: Option<String>,
pub to: String,
pub sdp: Option<String>,
pub credentials: Option<Credentials>,
pub pai_override: PaiOverride,
pub contact_uri: Option<String>,
pub outbound_proxy_override: ProxyOverride,
pub subject: Option<String>,
pub from_display: Option<String>,
pub precomputed_auth: Option<String>,
pub transfer_leg: Option<CallId>,
pub supported_100rel: bool,
pub extra_headers: Vec<rvoip_sip_core::types::TypedHeader>,
pub topology_hiding: bool,
}
pub struct OutboundCallBuilder {
coord: Arc<UnifiedCoordinator>,
from: Option<String>,
to: String,
sdp: Option<String>,
credentials: Option<Credentials>,
pai: PaiOverride,
contact_uri: Option<String>,
outbound_proxy: ProxyOverride,
subject: Option<String>,
from_display: Option<String>,
precomputed_authorization: Option<String>,
transfer_leg: Option<CallId>,
supported_100rel: bool,
state: BuilderHeaderState,
topology_hiding: bool,
}
impl OutboundCallBuilder {
pub(crate) fn new(
coord: Arc<UnifiedCoordinator>,
from: Option<String>,
to: impl Into<String>,
) -> Self {
Self {
coord,
from,
to: to.into(),
sdp: None,
credentials: None,
pai: PaiOverride::default(),
contact_uri: None,
outbound_proxy: ProxyOverride::default(),
subject: None,
from_display: None,
precomputed_authorization: None,
transfer_leg: None,
supported_100rel: false,
state: BuilderHeaderState::default(),
topology_hiding: false,
}
}
pub fn with_sdp(mut self, sdp: impl Into<String>) -> Self {
self.sdp = Some(sdp.into());
self
}
pub fn with_credentials(mut self, creds: Credentials) -> Self {
self.credentials = Some(creds);
self
}
pub fn with_pai(mut self, uri: impl Into<String>) -> Self {
self.pai = PaiOverride::Use(uri.into());
self
}
pub fn without_pai(mut self) -> Self {
self.pai = PaiOverride::Suppress;
self
}
pub fn with_contact_uri(mut self, uri: impl Into<String>) -> Self {
self.contact_uri = Some(uri.into());
self
}
pub fn with_outbound_proxy(mut self, uri: impl Into<String>) -> Self {
self.outbound_proxy = ProxyOverride::Use(uri.into());
self
}
pub fn without_outbound_proxy(mut self) -> Self {
self.outbound_proxy = ProxyOverride::Suppress;
self
}
pub fn with_subject(mut self, subject: impl Into<String>) -> Self {
self.subject = Some(subject.into());
self
}
pub fn with_from_display(mut self, display: impl Into<String>) -> Self {
self.from_display = Some(display.into());
self
}
pub fn with_precomputed_authorization(mut self, value: impl Into<String>) -> Self {
self.precomputed_authorization = Some(value.into());
self
}
pub fn as_transfer_leg(mut self, transferor: &CallId) -> Self {
self.transfer_leg = Some(transferor.clone());
self
}
pub fn with_supported_100rel(mut self, supported: bool) -> Self {
self.supported_100rel = supported;
self
}
pub fn with_topology_hiding(mut self, enabled: bool) -> Self {
self.topology_hiding = enabled;
self
}
pub async fn send(self) -> Result<CallId> {
let from = self
.from
.clone()
.unwrap_or_else(|| self.coord.config_local_uri());
let to = self.to.clone();
let pai_uri = match &self.pai {
PaiOverride::Use(uri) => Some(uri.clone()),
PaiOverride::Suppress => None,
PaiOverride::Default => self.coord.config_pai_uri(),
};
let credentials = self
.credentials
.clone()
.or_else(|| self.coord.config_credentials());
let snapshot = std::sync::Arc::new(OutboundCallOptionsSnapshot {
from: Some(from.clone()),
to: to.clone(),
sdp: self.sdp,
credentials,
pai_override: self.pai,
contact_uri: self.contact_uri,
outbound_proxy_override: self.outbound_proxy,
subject: self.subject,
from_display: self.from_display,
precomputed_auth: self.precomputed_authorization,
transfer_leg: self.transfer_leg,
supported_100rel: self.supported_100rel,
extra_headers: self.state.headers.clone(),
topology_hiding: self.topology_hiding,
});
let session_id = crate::state_table::SessionId::new();
self.coord
.helpers
.create_session(
session_id.clone(),
from.clone(),
to.clone(),
crate::state_table::Role::UAC,
)
.await?;
if snapshot.credentials.is_some()
|| pai_uri.is_some()
|| snapshot.transfer_leg.is_some()
|| !snapshot.extra_headers.is_empty()
{
let mut session = self.coord.session_state(&session_id).await?;
if let Some(c) = snapshot.credentials.clone() {
session.credentials = Some(c);
}
if let Some(pai) = pai_uri {
session.pai_uri = Some(pai);
}
if let Some(transferor) = snapshot.transfer_leg.clone() {
session.transferor_session_id = Some(transferor);
session.is_transfer_call = true;
}
if !snapshot.extra_headers.is_empty() {
session.extra_headers = snapshot.extra_headers.clone();
}
self.coord
.helpers
.state_machine
.store
.update_session(session)
.await?;
}
self.coord
.stage_outbound_options(
&session_id,
crate::state_machine::executor::PendingOptionsSlot::Invite(snapshot),
)
.await?;
self.coord
.dispatch_outbound(
&session_id,
crate::state_table::EventType::SendOutboundInvite,
)
.await?;
Ok(session_id)
}
}
impl SipRequestOptions for OutboundCallBuilder {
fn method(&self) -> Method {
Method::Invite
}
fn header_state_mut(&mut self) -> &mut BuilderHeaderState {
&mut self.state
}
fn header_state(&self) -> &BuilderHeaderState {
&self.state
}
}