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::trunks::SipTrunk;
use crate::resources::sip::{SipAuth, SipMediaEncryption, SipRegion, SipTransport};
const PATH: &str = "/v2/sip/phone-numbers";
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PhoneNumberInfo {
pub phone_number_id: String,
pub e164: String,
pub country_code: Option<String>,
pub provider: Option<String>,
pub origin: Option<String>,
pub status: Option<String>,
pub name: Option<String>,
pub created_at: Option<String>,
pub updated_at: Option<String>,
#[serde(flatten)]
pub extra: Map<String, Value>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PhoneNumberWithGateways {
pub phone_number: PhoneNumberInfo,
pub inbound: Option<SipTrunk>,
pub outbound: Option<SipTrunk>,
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PhoneNumberInboundConfig {
pub sip_region: SipRegion,
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PhoneNumberOutboundConfig {
pub address: String,
pub sip_region: SipRegion,
#[serde(skip_serializing_if = "Option::is_none")]
pub auth: Option<SipAuth>,
#[serde(skip_serializing_if = "Option::is_none")]
pub transport: Option<SipTransport>,
}
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CreatePhoneNumbersParams {
pub name: String,
pub phone_numbers: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_shared: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub media_encryption: Option<SipMediaEncryption>,
pub inbound: PhoneNumberInboundConfig,
pub outbound: PhoneNumberOutboundConfig,
}
#[derive(Debug, Clone, Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct UpdatePhoneNumberGatewayParams {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub media_encryption: Option<SipMediaEncryption>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sip_region: Option<SipRegion>,
#[serde(skip_serializing_if = "Option::is_none")]
pub address: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub auth: Option<SipAuth>,
#[serde(skip_serializing_if = "Option::is_none")]
pub transport: Option<SipTransport>,
}
#[derive(Debug, Clone, Default)]
pub struct PhoneNumberListParams {
pub page: Option<u32>,
pub per_page: Option<u32>,
pub cursor: Option<String>,
pub search: Option<String>,
}
impl PhoneNumberListParams {
fn pagination(&self) -> ListParams {
ListParams {
page: self.page,
per_page: self.per_page,
cursor: self.cursor.clone(),
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct PhoneNumbersResource<'a> {
client: &'a Client,
}
impl<'a> PhoneNumbersResource<'a> {
pub(crate) fn new(client: &'a Client) -> Self {
Self { client }
}
pub async fn create(&self, params: CreatePhoneNumbersParams) -> Result<MessageResponse> {
self.client
.json(Method::POST, PATH, CallOptions::json(¶ms)?)
.await
}
pub async fn list(
&self,
params: PhoneNumberListParams,
) -> Result<Page<PhoneNumberWithGateways>> {
paginate(self.fetcher(¶ms), ¶ms.pagination(), "data", None).await
}
pub fn list_stream(
&self,
params: PhoneNumberListParams,
) -> impl Stream<Item = Result<PhoneNumberWithGateways>> + Send {
auto_page(self.fetcher(¶ms), params.pagination(), "data", None)
}
pub async fn get(&self, number: &str) -> Result<PhoneNumberWithGateways> {
let path = format!("{PATH}/{}", escape(number));
self.client
.json(Method::GET, &path, CallOptions::new())
.await
}
pub async fn detach(&self, phone_number: &str) -> Result<MessageResponse> {
let body = serde_json::json!({ "phoneNumber": phone_number });
let path = format!("{PATH}/detach");
self.client
.json(Method::POST, &path, CallOptions::json(&body)?)
.await
}
pub async fn update_gateway(
&self,
gateway_id: &str,
params: UpdatePhoneNumberGatewayParams,
) -> Result<MessageResponse> {
let path = format!("{PATH}/gateways/{}", escape(gateway_id));
self.client
.json(Method::PATCH, &path, CallOptions::json(¶ms)?)
.await
}
pub async fn release(&self, phone_number_id: &str) -> Result<MessageResponse> {
let path = format!("{PATH}/{}/release", escape(phone_number_id));
self.client
.json(Method::POST, &path, CallOptions::new())
.await
}
fn fetcher(&self, params: &PhoneNumberListParams) -> PageFetcher {
let client = self.client.clone();
let search = params.search.clone();
Arc::new(move |page, per_page| {
let client = client.clone();
let search = search.clone();
Box::pin(async move {
let query = QueryBuilder::new()
.opt("page", page)
.opt("perPage", per_page)
.opt_str("search", search.as_deref())
.into_pairs();
client
.json::<Value>(Method::GET, PATH, CallOptions::new().query(query))
.await
})
})
}
}