use std::collections::HashMap;
use std::sync::Arc;
use futures_util::Stream;
use reqwest::Method;
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
use crate::client::{CallOptions, Client};
use crate::common::MessageResponse;
use crate::error::Result;
use crate::pagination::{auto_page, paginate, ListParams, Page, PageFetcher};
use crate::query::QueryBuilder;
use crate::resources::escape;
use crate::resources::sip::{SipAuth, SipMediaEncryption, SipRegion, SipTransport};
const INBOUND_PATH: &str = "/v2/sip/inbound-gateways";
const OUTBOUND_PATH: &str = "/v2/sip/outbound-gateways";
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SipTrunk {
pub id: String,
pub name: Option<String>,
pub address: Option<String>,
#[serde(default, deserialize_with = "crate::common::null_to_default")]
pub numbers: Vec<String>,
#[serde(default, deserialize_with = "crate::common::null_to_default")]
pub allowed_addresses: Vec<String>,
#[serde(default, deserialize_with = "crate::common::null_to_default")]
pub allowed_numbers: Vec<String>,
pub media_encryption: Option<SipMediaEncryption>,
pub transport: Option<SipTransport>,
pub record: Option<bool>,
#[serde(default, deserialize_with = "crate::common::null_to_default")]
pub tags: Vec<String>,
pub auth: Option<SipAuth>,
pub geo_region: Option<SipRegion>,
#[serde(default, deserialize_with = "crate::common::null_to_default")]
pub metadata: HashMap<String, String>,
pub noise_cancellation: Option<bool>,
pub enable_dtmf: Option<bool>,
#[serde(flatten)]
pub extra: Map<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CreateInboundTrunkParams {
pub name: String,
pub numbers: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub auth: Option<SipAuth>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub allowed_addresses: Vec<String>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub allowed_numbers: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub media_encryption: Option<SipMediaEncryption>,
#[serde(skip_serializing_if = "Option::is_none")]
pub record: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub noise_cancellation: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub enable_dtmf: Option<bool>,
#[serde(skip_serializing_if = "HashMap::is_empty")]
pub metadata: HashMap<String, String>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub tags: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub geo_region: Option<SipRegion>,
}
#[derive(Debug, Clone, Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct UpdateInboundTrunkParams {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub numbers: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub auth: Option<SipAuth>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub allowed_addresses: Vec<String>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub allowed_numbers: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub media_encryption: Option<SipMediaEncryption>,
#[serde(skip_serializing_if = "Option::is_none")]
pub record: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub noise_cancellation: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub enable_dtmf: Option<bool>,
#[serde(skip_serializing_if = "HashMap::is_empty")]
pub metadata: HashMap<String, String>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub tags: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub geo_region: Option<SipRegion>,
}
#[derive(Debug, Clone, Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CreateOutboundTrunkParams {
pub name: String,
pub address: String,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub numbers: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub transport: Option<SipTransport>,
#[serde(skip_serializing_if = "Option::is_none")]
pub auth: Option<SipAuth>,
#[serde(skip_serializing_if = "Option::is_none")]
pub media_encryption: Option<SipMediaEncryption>,
#[serde(skip_serializing_if = "Option::is_none")]
pub record: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub noise_cancellation: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub enable_dtmf: Option<bool>,
#[serde(skip_serializing_if = "HashMap::is_empty")]
pub metadata: HashMap<String, String>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub tags: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub geo_region: Option<SipRegion>,
}
#[derive(Debug, Clone, Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct UpdateOutboundTrunkParams {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub numbers: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub address: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub transport: Option<SipTransport>,
#[serde(skip_serializing_if = "Option::is_none")]
pub auth: Option<SipAuth>,
#[serde(skip_serializing_if = "Option::is_none")]
pub media_encryption: Option<SipMediaEncryption>,
#[serde(skip_serializing_if = "Option::is_none")]
pub record: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub noise_cancellation: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub enable_dtmf: Option<bool>,
#[serde(skip_serializing_if = "HashMap::is_empty")]
pub metadata: HashMap<String, String>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub tags: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub geo_region: Option<SipRegion>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub allowed_numbers: Vec<String>,
}
#[derive(Debug, Clone, Default)]
pub struct SipTrunkListParams {
pub page: Option<u32>,
pub per_page: Option<u32>,
pub cursor: Option<String>,
pub id: Option<String>,
pub search: Option<String>,
}
impl SipTrunkListParams {
fn pagination(&self) -> ListParams {
ListParams {
page: self.page,
per_page: self.per_page,
cursor: self.cursor.clone(),
}
}
}
fn trunk_fetcher(client: &Client, path: &'static str, params: &SipTrunkListParams) -> PageFetcher {
let client = client.clone();
let params = params.clone();
Arc::new(move |page, per_page| {
let client = client.clone();
let params = params.clone();
Box::pin(async move {
let query = QueryBuilder::new()
.opt("page", page)
.opt("perPage", per_page)
.opt_str("id", params.id.as_deref())
.opt_str("search", params.search.as_deref())
.into_pairs();
client
.json::<Value>(Method::GET, path, CallOptions::new().query(query))
.await
})
})
}
#[derive(Debug, Clone, Copy)]
pub struct InboundTrunkResource<'a> {
client: &'a Client,
}
impl<'a> InboundTrunkResource<'a> {
pub async fn create(&self, params: CreateInboundTrunkParams) -> Result<SipTrunk> {
self.client
.json(Method::POST, INBOUND_PATH, CallOptions::json(¶ms)?)
.await
}
pub async fn list(&self, params: SipTrunkListParams) -> Result<Page<SipTrunk>> {
let fetcher = trunk_fetcher(self.client, INBOUND_PATH, ¶ms);
paginate(fetcher, ¶ms.pagination(), "data", None).await
}
pub fn list_stream(
&self,
params: SipTrunkListParams,
) -> impl Stream<Item = Result<SipTrunk>> + Send {
let fetcher = trunk_fetcher(self.client, INBOUND_PATH, ¶ms);
auto_page(fetcher, params.pagination(), "data", None)
}
pub async fn get(&self, trunk_id: &str) -> Result<SipTrunk> {
let path = format!("{INBOUND_PATH}/{}", escape(trunk_id));
self.client
.json(Method::GET, &path, CallOptions::new())
.await
}
pub async fn update(
&self,
trunk_id: &str,
params: UpdateInboundTrunkParams,
) -> Result<SipTrunk> {
let path = format!("{INBOUND_PATH}/{}", escape(trunk_id));
self.client
.json(Method::PATCH, &path, CallOptions::json(¶ms)?)
.await
}
pub async fn delete(&self, trunk_id: &str) -> Result<MessageResponse> {
let path = format!("{INBOUND_PATH}/{}", escape(trunk_id));
self.client
.json(Method::DELETE, &path, CallOptions::new())
.await
}
}
#[derive(Debug, Clone, Copy)]
pub struct OutboundTrunkResource<'a> {
client: &'a Client,
}
impl<'a> OutboundTrunkResource<'a> {
pub async fn create(&self, params: CreateOutboundTrunkParams) -> Result<SipTrunk> {
self.client
.json(Method::POST, OUTBOUND_PATH, CallOptions::json(¶ms)?)
.await
}
pub async fn list(&self, params: SipTrunkListParams) -> Result<Page<SipTrunk>> {
let fetcher = trunk_fetcher(self.client, OUTBOUND_PATH, ¶ms);
paginate(fetcher, ¶ms.pagination(), "data", None).await
}
pub fn list_stream(
&self,
params: SipTrunkListParams,
) -> impl Stream<Item = Result<SipTrunk>> + Send {
let fetcher = trunk_fetcher(self.client, OUTBOUND_PATH, ¶ms);
auto_page(fetcher, params.pagination(), "data", None)
}
pub async fn get(&self, trunk_id: &str) -> Result<SipTrunk> {
let path = format!("{OUTBOUND_PATH}/{}", escape(trunk_id));
self.client
.json(Method::GET, &path, CallOptions::new())
.await
}
pub async fn update(
&self,
trunk_id: &str,
params: UpdateOutboundTrunkParams,
) -> Result<SipTrunk> {
let path = format!("{OUTBOUND_PATH}/{}", escape(trunk_id));
self.client
.json(Method::PATCH, &path, CallOptions::json(¶ms)?)
.await
}
pub async fn delete(&self, trunk_id: &str) -> Result<MessageResponse> {
let path = format!("{OUTBOUND_PATH}/{}", escape(trunk_id));
self.client
.json(Method::DELETE, &path, CallOptions::new())
.await
}
}
#[derive(Debug, Clone, Copy)]
pub struct SipTrunksResource<'a> {
client: &'a Client,
}
impl<'a> SipTrunksResource<'a> {
pub(crate) fn new(client: &'a Client) -> Self {
Self { client }
}
pub fn inbound(&self) -> InboundTrunkResource<'a> {
InboundTrunkResource {
client: self.client,
}
}
pub fn outbound(&self) -> OutboundTrunkResource<'a> {
OutboundTrunkResource {
client: self.client,
}
}
}