1use std::collections::HashMap;
4use std::sync::Arc;
5
6use futures_util::Stream;
7use reqwest::Method;
8use serde::{Deserialize, Serialize};
9use serde_json::{Map, Value};
10
11use crate::client::{CallOptions, Client};
12use crate::common::MessageResponse;
13use crate::error::Result;
14use crate::pagination::{auto_page, paginate, ListParams, Page, PageFetcher};
15use crate::query::QueryBuilder;
16use crate::resources::escape;
17use crate::resources::sip::{SipAuth, SipMediaEncryption, SipRegion, SipTransport};
18
19const INBOUND_PATH: &str = "/v2/sip/inbound-gateways";
20const OUTBOUND_PATH: &str = "/v2/sip/outbound-gateways";
21
22#[derive(Debug, Clone, Deserialize)]
24#[serde(rename_all = "camelCase")]
25pub struct SipTrunk {
26 pub id: String,
28 pub name: Option<String>,
30 pub address: Option<String>,
32 #[serde(default, deserialize_with = "crate::common::null_to_default")]
34 pub numbers: Vec<String>,
35 #[serde(default, deserialize_with = "crate::common::null_to_default")]
37 pub allowed_addresses: Vec<String>,
38 #[serde(default, deserialize_with = "crate::common::null_to_default")]
40 pub allowed_numbers: Vec<String>,
41 pub media_encryption: Option<SipMediaEncryption>,
43 pub transport: Option<SipTransport>,
45 pub record: Option<bool>,
47 #[serde(default, deserialize_with = "crate::common::null_to_default")]
49 pub tags: Vec<String>,
50 pub auth: Option<SipAuth>,
52 pub geo_region: Option<SipRegion>,
54 #[serde(default, deserialize_with = "crate::common::null_to_default")]
56 pub metadata: HashMap<String, String>,
57 pub noise_cancellation: Option<bool>,
59 pub enable_dtmf: Option<bool>,
61 #[serde(flatten)]
63 pub extra: Map<String, Value>,
64}
65
66#[derive(Debug, Clone, Default, Serialize)]
68#[serde(rename_all = "camelCase")]
69pub struct CreateInboundTrunkParams {
70 pub name: String,
72 pub numbers: Vec<String>,
74 #[serde(skip_serializing_if = "Option::is_none")]
76 pub auth: Option<SipAuth>,
77 #[serde(skip_serializing_if = "Vec::is_empty")]
79 pub allowed_addresses: Vec<String>,
80 #[serde(skip_serializing_if = "Vec::is_empty")]
82 pub allowed_numbers: Vec<String>,
83 #[serde(skip_serializing_if = "Option::is_none")]
85 pub media_encryption: Option<SipMediaEncryption>,
86 #[serde(skip_serializing_if = "Option::is_none")]
88 pub record: Option<bool>,
89 #[serde(skip_serializing_if = "Option::is_none")]
91 pub noise_cancellation: Option<bool>,
92 #[serde(skip_serializing_if = "Option::is_none")]
94 pub enable_dtmf: Option<bool>,
95 #[serde(skip_serializing_if = "HashMap::is_empty")]
97 pub metadata: HashMap<String, String>,
98 #[serde(skip_serializing_if = "Vec::is_empty")]
100 pub tags: Vec<String>,
101 #[serde(skip_serializing_if = "Option::is_none")]
103 pub geo_region: Option<SipRegion>,
104}
105
106#[derive(Debug, Clone, Default, Serialize)]
112#[serde(rename_all = "camelCase")]
113pub struct UpdateInboundTrunkParams {
114 #[serde(skip_serializing_if = "Option::is_none")]
116 pub name: Option<String>,
117 #[serde(skip_serializing_if = "Vec::is_empty")]
119 pub numbers: Vec<String>,
120 #[serde(skip_serializing_if = "Option::is_none")]
122 pub auth: Option<SipAuth>,
123 #[serde(skip_serializing_if = "Vec::is_empty")]
125 pub allowed_addresses: Vec<String>,
126 #[serde(skip_serializing_if = "Vec::is_empty")]
128 pub allowed_numbers: Vec<String>,
129 #[serde(skip_serializing_if = "Option::is_none")]
131 pub media_encryption: Option<SipMediaEncryption>,
132 #[serde(skip_serializing_if = "Option::is_none")]
134 pub record: Option<bool>,
135 #[serde(skip_serializing_if = "Option::is_none")]
137 pub noise_cancellation: Option<bool>,
138 #[serde(skip_serializing_if = "Option::is_none")]
140 pub enable_dtmf: Option<bool>,
141 #[serde(skip_serializing_if = "HashMap::is_empty")]
143 pub metadata: HashMap<String, String>,
144 #[serde(skip_serializing_if = "Vec::is_empty")]
146 pub tags: Vec<String>,
147 #[serde(skip_serializing_if = "Option::is_none")]
149 pub geo_region: Option<SipRegion>,
150}
151
152#[derive(Debug, Clone, Default, Serialize)]
154#[serde(rename_all = "camelCase")]
155pub struct CreateOutboundTrunkParams {
156 pub name: String,
158 pub address: String,
160 #[serde(skip_serializing_if = "Vec::is_empty")]
163 pub numbers: Vec<String>,
164 #[serde(skip_serializing_if = "Option::is_none")]
166 pub transport: Option<SipTransport>,
167 #[serde(skip_serializing_if = "Option::is_none")]
169 pub auth: Option<SipAuth>,
170 #[serde(skip_serializing_if = "Option::is_none")]
172 pub media_encryption: Option<SipMediaEncryption>,
173 #[serde(skip_serializing_if = "Option::is_none")]
175 pub record: Option<bool>,
176 #[serde(skip_serializing_if = "Option::is_none")]
178 pub noise_cancellation: Option<bool>,
179 #[serde(skip_serializing_if = "Option::is_none")]
181 pub enable_dtmf: Option<bool>,
182 #[serde(skip_serializing_if = "HashMap::is_empty")]
184 pub metadata: HashMap<String, String>,
185 #[serde(skip_serializing_if = "Vec::is_empty")]
187 pub tags: Vec<String>,
188 #[serde(skip_serializing_if = "Option::is_none")]
190 pub geo_region: Option<SipRegion>,
191}
192
193#[derive(Debug, Clone, Default, Serialize)]
196#[serde(rename_all = "camelCase")]
197pub struct UpdateOutboundTrunkParams {
198 #[serde(skip_serializing_if = "Option::is_none")]
200 pub name: Option<String>,
201 #[serde(skip_serializing_if = "Vec::is_empty")]
203 pub numbers: Vec<String>,
204 #[serde(skip_serializing_if = "Option::is_none")]
206 pub address: Option<String>,
207 #[serde(skip_serializing_if = "Option::is_none")]
209 pub transport: Option<SipTransport>,
210 #[serde(skip_serializing_if = "Option::is_none")]
212 pub auth: Option<SipAuth>,
213 #[serde(skip_serializing_if = "Option::is_none")]
215 pub media_encryption: Option<SipMediaEncryption>,
216 #[serde(skip_serializing_if = "Option::is_none")]
218 pub record: Option<bool>,
219 #[serde(skip_serializing_if = "Option::is_none")]
221 pub noise_cancellation: Option<bool>,
222 #[serde(skip_serializing_if = "Option::is_none")]
224 pub enable_dtmf: Option<bool>,
225 #[serde(skip_serializing_if = "HashMap::is_empty")]
227 pub metadata: HashMap<String, String>,
228 #[serde(skip_serializing_if = "Vec::is_empty")]
230 pub tags: Vec<String>,
231 #[serde(skip_serializing_if = "Option::is_none")]
233 pub geo_region: Option<SipRegion>,
234 #[serde(skip_serializing_if = "Vec::is_empty")]
236 pub allowed_numbers: Vec<String>,
237}
238
239#[derive(Debug, Clone, Default)]
241pub struct SipTrunkListParams {
242 pub page: Option<u32>,
244 pub per_page: Option<u32>,
246 pub cursor: Option<String>,
248 pub id: Option<String>,
250 pub search: Option<String>,
252}
253
254impl SipTrunkListParams {
255 fn pagination(&self) -> ListParams {
256 ListParams {
257 page: self.page,
258 per_page: self.per_page,
259 cursor: self.cursor.clone(),
260 }
261 }
262}
263
264fn trunk_fetcher(client: &Client, path: &'static str, params: &SipTrunkListParams) -> PageFetcher {
265 let client = client.clone();
266 let params = params.clone();
267 Arc::new(move |page, per_page| {
268 let client = client.clone();
269 let params = params.clone();
270 Box::pin(async move {
271 let query = QueryBuilder::new()
272 .opt("page", page)
273 .opt("perPage", per_page)
274 .opt_str("id", params.id.as_deref())
275 .opt_str("search", params.search.as_deref())
276 .into_pairs();
277 client
278 .json::<Value>(Method::GET, path, CallOptions::new().query(query))
279 .await
280 })
281 })
282}
283
284#[derive(Debug, Clone, Copy)]
287pub struct InboundTrunkResource<'a> {
288 client: &'a Client,
289}
290
291impl<'a> InboundTrunkResource<'a> {
292 pub async fn create(&self, params: CreateInboundTrunkParams) -> Result<SipTrunk> {
294 self.client
295 .json(Method::POST, INBOUND_PATH, CallOptions::json(¶ms)?)
296 .await
297 }
298
299 pub async fn list(&self, params: SipTrunkListParams) -> Result<Page<SipTrunk>> {
301 let fetcher = trunk_fetcher(self.client, INBOUND_PATH, ¶ms);
302 paginate(fetcher, ¶ms.pagination(), "data", None).await
303 }
304
305 pub fn list_stream(
307 &self,
308 params: SipTrunkListParams,
309 ) -> impl Stream<Item = Result<SipTrunk>> + Send {
310 let fetcher = trunk_fetcher(self.client, INBOUND_PATH, ¶ms);
311 auto_page(fetcher, params.pagination(), "data", None)
312 }
313
314 pub async fn get(&self, trunk_id: &str) -> Result<SipTrunk> {
316 let path = format!("{INBOUND_PATH}/{}", escape(trunk_id));
317 self.client
318 .json(Method::GET, &path, CallOptions::new())
319 .await
320 }
321
322 pub async fn update(
325 &self,
326 trunk_id: &str,
327 params: UpdateInboundTrunkParams,
328 ) -> Result<SipTrunk> {
329 let path = format!("{INBOUND_PATH}/{}", escape(trunk_id));
330 self.client
331 .json(Method::PATCH, &path, CallOptions::json(¶ms)?)
332 .await
333 }
334
335 pub async fn delete(&self, trunk_id: &str) -> Result<MessageResponse> {
337 let path = format!("{INBOUND_PATH}/{}", escape(trunk_id));
338 self.client
339 .json(Method::DELETE, &path, CallOptions::new())
340 .await
341 }
342}
343
344#[derive(Debug, Clone, Copy)]
347pub struct OutboundTrunkResource<'a> {
348 client: &'a Client,
349}
350
351impl<'a> OutboundTrunkResource<'a> {
352 pub async fn create(&self, params: CreateOutboundTrunkParams) -> Result<SipTrunk> {
354 self.client
355 .json(Method::POST, OUTBOUND_PATH, CallOptions::json(¶ms)?)
356 .await
357 }
358
359 pub async fn list(&self, params: SipTrunkListParams) -> Result<Page<SipTrunk>> {
361 let fetcher = trunk_fetcher(self.client, OUTBOUND_PATH, ¶ms);
362 paginate(fetcher, ¶ms.pagination(), "data", None).await
363 }
364
365 pub fn list_stream(
367 &self,
368 params: SipTrunkListParams,
369 ) -> impl Stream<Item = Result<SipTrunk>> + Send {
370 let fetcher = trunk_fetcher(self.client, OUTBOUND_PATH, ¶ms);
371 auto_page(fetcher, params.pagination(), "data", None)
372 }
373
374 pub async fn get(&self, trunk_id: &str) -> Result<SipTrunk> {
376 let path = format!("{OUTBOUND_PATH}/{}", escape(trunk_id));
377 self.client
378 .json(Method::GET, &path, CallOptions::new())
379 .await
380 }
381
382 pub async fn update(
385 &self,
386 trunk_id: &str,
387 params: UpdateOutboundTrunkParams,
388 ) -> Result<SipTrunk> {
389 let path = format!("{OUTBOUND_PATH}/{}", escape(trunk_id));
390 self.client
391 .json(Method::PATCH, &path, CallOptions::json(¶ms)?)
392 .await
393 }
394
395 pub async fn delete(&self, trunk_id: &str) -> Result<MessageResponse> {
397 let path = format!("{OUTBOUND_PATH}/{}", escape(trunk_id));
398 self.client
399 .json(Method::DELETE, &path, CallOptions::new())
400 .await
401 }
402}
403
404#[derive(Debug, Clone, Copy)]
407pub struct SipTrunksResource<'a> {
408 client: &'a Client,
409}
410
411impl<'a> SipTrunksResource<'a> {
412 pub(crate) fn new(client: &'a Client) -> Self {
413 Self { client }
414 }
415
416 pub fn inbound(&self) -> InboundTrunkResource<'a> {
418 InboundTrunkResource {
419 client: self.client,
420 }
421 }
422
423 pub fn outbound(&self) -> OutboundTrunkResource<'a> {
425 OutboundTrunkResource {
426 client: self.client,
427 }
428 }
429}