twiml_rust/
voice.rs

1//! TwiML generation for voice call responses.
2//!
3//! This module provides comprehensive support for creating TwiML responses
4//! for voice calls. The main entry point is [`VoiceResponse`], which can
5//! contain various voice verbs like [`Say`], [`Play`], [`Dial`], [`Gather`],
6//! [`Record`], and many more.
7//!
8//! # Examples
9//!
10//! ## Simple Voice Response
11//!
12//! ```rust
13//! use twiml_rust::{VoiceResponse, TwiML};
14//!
15//! let response = VoiceResponse::new()
16//!     .say("Hello! Welcome to our service.")
17//!     .play("https://example.com/music.mp3")
18//!     .hangup();
19//!
20//! println!("{}", response.to_xml());
21//! ```
22//!
23//! ## Interactive Voice Response (IVR)
24//!
25//! ```rust
26//! use twiml_rust::{VoiceResponse, voice::{Gather, Say}, TwiML};
27//!
28//! let gather = Gather::new()
29//!     .input(vec!["dtmf".to_string(), "speech".to_string()])
30//!     .action("https://example.com/process")
31//!     .timeout(10)
32//!     .add_say(Say::new("Press 1 for sales, 2 for support"));
33//!
34//! let response = VoiceResponse::new()
35//!     .say("Welcome!")
36//!     .gather(gather)
37//!     .hangup();
38//!
39//! println!("{}", response.to_xml());
40//! ```
41//!
42//! ## Call Forwarding
43//!
44//! ```rust
45//! use twiml_rust::{VoiceResponse, voice::{Dial, DialNumber}, TwiML};
46//!
47//! let dial = Dial::new()
48//!     .timeout(30)
49//!     .add_number(DialNumber::new("+15551234567"));
50//!
51//! let response = VoiceResponse::new()
52//!     .say("Connecting your call...")
53//!     .dial_with(dial);
54//!
55//! println!("{}", response.to_xml());
56//! ```
57
58use crate::xml_escape::{escape_xml_attr, escape_xml_text};
59use crate::TwiML;
60
61// ===================================
62// Type Aliases
63// ===================================
64
65pub type ApplicationEvent = &'static str; // "initiated" | "ringing" | "answered" | "completed"
66pub type ClientEvent = &'static str; // "initiated" | "ringing" | "answered" | "completed"
67pub type ConferenceBeep = &'static str; // "true" | "false" | "onEnter" | "onExit"
68pub type ConferenceEvent = &'static str; // "start" | "end" | "join" | "leave" | "mute" | "hold" | "modify" | "speaker" | "announcement"
69pub type ConferenceJitterBufferSize = &'static str; // "large" | "medium" | "small" | "off"
70pub type ConferenceRecord = &'static str; // "do-not-record" | "record-from-start"
71pub type ConferenceRecordingEvent = &'static str; // "in-progress" | "completed" | "absent"
72pub type ConferenceRegion = &'static str; // "us1" | "us2" | "ie1" | "sg1" | "br1" | "au1" | "jp1" | "de1"
73pub type ConferenceTrim = &'static str; // "trim-silence" | "do-not-trim"
74pub type ConversationEvent = &'static str; // "call-initiated" | "call-ringing" | "call-answered" | "call-completed"
75pub type ConversationRecord = &'static str; // "do-not-record" | "record-from-answer" | "record-from-ringing" | "record-from-answer-dual" | "record-from-ringing-dual" | "true" | "false"
76pub type ConversationRecordingEvent = &'static str; // "in-progress" | "completed" | "absent"
77pub type ConversationTrim = &'static str; // "trim-silence" | "do-not-trim"
78pub type DialEvents = &'static str; // "call-progress-event"
79pub type DialRecord = &'static str; // "do-not-record" | "record-from-answer" | "record-from-ringing" | "record-from-answer-dual" | "record-from-ringing-dual"
80pub type DialRecordingEvent = &'static str; // "in-progress" | "completed" | "absent"
81pub type DialRecordingTrack = &'static str; // "both" | "inbound" | "outbound"
82pub type DialRingTone = &'static str; // "at" | "au" | "bg" | "br" | "be" | "ch" | "cl" | "cn" | "cz" | "de" | "dk" | "ee" | "es" | "fi" | "fr" | "gr" | "hu" | "il" | "in" | "it" | "lt" | "jp" | "mx" | "my" | "nl" | "no" | "nz" | "ph" | "pl" | "pt" | "ru" | "se" | "sg" | "th" | "uk" | "us" | "us-old" | "tw" | "ve" | "za"
83pub type DialTrim = &'static str; // "trim-silence" | "do-not-trim"
84pub type GatherInput = &'static str; // "dtmf" | "speech"
85pub type GatherLanguage = &'static str; // "af-ZA" | "am-ET" | "ar-AE" | ... (many language codes)
86pub type NumberEvent = &'static str; // "initiated" | "ringing" | "answered" | "completed"
87pub type PayBankAccountType = &'static str; // "consumer-checking" | "consumer-savings" | "commercial-checking" | "commercial-savings"
88pub type PayInput = &'static str; // "dtmf"
89pub type PayLanguage = &'static str; // "de-DE" | "en-AU" | "en-CA" | "en-GB" | "en-IN" | "en-IE" | "en-NZ" | "en-PH" | "en-ZA" | "en-US" | "es-ES" | "es-US" | "fr-CA" | "fr-FR" | "it-IT"
90pub type PayPaymentMethod = &'static str; // "ach-debit" | "credit-card"
91pub type PayStatusCallbackMethod = &'static str; // "GET" | "POST"
92pub type PayTokenType = &'static str; // "one-time" | "reusable" | "payment-method"
93pub type PayValidCardTypes = &'static str; // "visa" | "mastercard" | "amex" | "maestro" | "discover" | "optima" | "jcb" | "diners-club" | "enroute"
94pub type PromptCardType = &'static str; // "visa" | "mastercard" | "amex" | "maestro" | "discover" | "optima" | "jcb" | "diners-club" | "enroute"
95pub type PromptErrorType = &'static str; // "timeout" | "invalid-card-number" | "invalid-card-type" | "invalid-date" | "invalid-security-code" | "internal-error" | "input-matching-failed"
96pub type PromptFor = &'static str; // "payment-card-number" | "expiration-date" | "security-code" | "postal-code" | "payment-processing" | "bank-account-number" | "bank-routing-number"
97pub type RecordRecordingEvent = &'static str; // "in-progress" | "completed" | "absent"
98pub type RecordTrim = &'static str; // "trim-silence" | "do-not-trim"
99pub type RecordingChannels = &'static str; // "mono" | "dual"
100pub type RecordingEvent = &'static str; // "in-progress" | "completed" | "absent"
101pub type RecordingRecordingStatusCallbackMethod = &'static str; // "GET" | "POST"
102pub type RecordingTrack = &'static str; // "inbound" | "outbound" | "both"
103pub type RecordingTrim = &'static str; // "trim-silence" | "do-not-trim"
104pub type RejectReason = &'static str; // "rejected" | "busy"
105pub type SayLanguage = &'static str; // "af-ZA" | "am-ET" | "ar-AE" | ... (many language codes)
106pub type SayVoice = &'static str; // "man" | "woman" | "alice" | "Google.*" | "Polly.*" | ... (many voices)
107pub type SipEvent = &'static str; // "initiated" | "ringing" | "answered" | "completed"
108pub type SiprecStatusCallbackMethod = &'static str; // "GET" | "POST"
109pub type SiprecTrack = &'static str; // "inbound_track" | "outbound_track" | "both_tracks"
110pub type SsmlBreakStrength = &'static str; // "none" | "x-weak" | "weak" | "medium" | "strong" | "x-strong"
111pub type SsmlEmphasisLevel = &'static str; // "strong" | "moderate" | "reduced"
112pub type SsmlLangXmlLang = &'static str; // "arb" | "ar-AE" | "ca-ES" | ... (many language codes)
113pub type SsmlPhonemeAlphabet = &'static str; // "ipa" | "x-sampa" | "x-amazon-jyutping" | "x-amazon-pinyin" | "x-amazon-pron-kana" | "x-amazon-yomigana"
114pub type SsmlSayAsFormat = &'static str; // "mdy" | "dmy" | "ymd" | "md" | "dm" | "ym" | "my" | "d" | "m" | "y" | "yyyymmdd"
115pub type SsmlSayAsInterpretAs = &'static str; // "characters" | "spell-out" | "cardinal" | "number" | "ordinal" | "digits" | "fraction" | "unit" | "date" | "time" | "address" | "expletive" | "telephone"
116pub type StreamStatusCallbackMethod = &'static str; // "GET" | "POST"
117pub type StreamTrack = &'static str; // "inbound_track" | "outbound_track" | "both_tracks"
118pub type TranscriptionStatusCallbackMethod = &'static str; // "GET" | "POST"
119pub type TranscriptionTrack = &'static str; // "inbound_track" | "outbound_track" | "both_tracks"
120pub type WhatsAppEvent = &'static str; // "initiated" | "ringing" | "answered" | "completed"
121
122// ================================================
123// Attribute Structs
124// ================================================
125
126#[derive(Debug, Clone, Default)]
127pub struct ConnectAttributes {
128    pub action: Option<String>,
129    pub method: Option<String>,
130}
131
132#[derive(Debug, Clone, Default)]
133pub struct DialAttributes {
134    pub action: Option<String>,
135    pub answer_on_bridge: Option<bool>,
136    pub caller_id: Option<String>,
137    pub call_reason: Option<String>,
138    pub events: Option<String>,
139    pub hangup_on_star: Option<bool>,
140    pub method: Option<String>,
141    pub record: Option<String>,
142    pub recording_status_callback: Option<String>,
143    pub recording_status_callback_event: Option<Vec<String>>,
144    pub recording_status_callback_method: Option<String>,
145    pub recording_track: Option<String>,
146    pub refer_method: Option<String>,
147    pub refer_url: Option<String>,
148    pub ring_tone: Option<String>,
149    pub sequential: Option<bool>,
150    pub time_limit: Option<u32>,
151    pub timeout: Option<u32>,
152    pub trim: Option<String>,
153}
154
155#[derive(Debug, Clone, Default)]
156pub struct EnqueueAttributes {
157    pub action: Option<String>,
158    pub max_queue_size: Option<u32>,
159    pub method: Option<String>,
160    pub wait_url: Option<String>,
161    pub wait_url_method: Option<String>,
162    pub workflow_sid: Option<String>,
163}
164
165#[derive(Debug, Clone, Default)]
166pub struct GatherAttributes {
167    pub action: Option<String>,
168    pub action_on_empty_result: Option<bool>,
169    pub barge_in: Option<bool>,
170    pub debug: Option<bool>,
171    pub dtmf_detection: Option<bool>,
172    pub enhanced: Option<bool>,
173    pub finish_on_key: Option<String>,
174    pub hints: Option<String>,
175    pub input: Option<Vec<String>>,
176    pub language: Option<String>,
177    pub max_speech_time: Option<u32>,
178    pub method: Option<String>,
179    pub num_digits: Option<u32>,
180    pub partial_result_callback: Option<String>,
181    pub partial_result_callback_method: Option<String>,
182    pub profanity_filter: Option<bool>,
183    pub speech_model: Option<String>,
184    pub speech_timeout: Option<String>,
185    pub timeout: Option<u32>,
186}
187
188#[derive(Debug, Clone, Default)]
189pub struct PauseAttributes {
190    pub length: Option<u32>,
191}
192
193#[derive(Debug, Clone, Default)]
194pub struct PayAttributes {
195    pub action: Option<String>,
196    pub bank_account_type: Option<String>,
197    pub charge_amount: Option<String>,
198    pub currency: Option<String>,
199    pub description: Option<String>,
200    pub input: Option<String>,
201    pub language: Option<String>,
202    pub max_attempts: Option<u32>,
203    pub method: Option<String>,
204    pub min_postal_code_length: Option<u32>,
205    pub payment_connector: Option<String>,
206    pub payment_method: Option<String>,
207    pub postal_code: Option<bool>,
208    pub security_code: Option<bool>,
209    pub status_callback: Option<String>,
210    pub status_callback_method: Option<String>,
211    pub timeout: Option<u32>,
212    pub token_type: Option<String>,
213    pub valid_card_types: Option<Vec<String>>,
214}
215
216#[derive(Debug, Clone, Default)]
217pub struct PlayAttributes {
218    pub digits: Option<String>,
219    pub loop_count: Option<u32>,
220}
221
222#[derive(Debug, Clone, Default)]
223pub struct PromptAttributes {
224    pub attempt: Option<Vec<u32>>,
225    pub card_type: Option<Vec<String>>,
226    pub error_type: Option<Vec<String>>,
227    pub for_attr: Option<String>,
228    pub require_matching_inputs: Option<bool>,
229}
230
231#[derive(Debug, Clone, Default)]
232pub struct QueueAttributes {
233    pub method: Option<String>,
234    pub post_work_activity_sid: Option<String>,
235    pub reservation_sid: Option<String>,
236    pub url: Option<String>,
237}
238
239#[derive(Debug, Clone, Default)]
240pub struct RecordAttributes {
241    pub action: Option<String>,
242    pub finish_on_key: Option<String>,
243    pub max_length: Option<u32>,
244    pub method: Option<String>,
245    pub play_beep: Option<bool>,
246    pub recording_status_callback: Option<String>,
247    pub recording_status_callback_event: Option<Vec<String>>,
248    pub recording_status_callback_method: Option<String>,
249    pub timeout: Option<u32>,
250    pub transcribe: Option<bool>,
251    pub transcribe_callback: Option<String>,
252    pub trim: Option<String>,
253    pub recording_channels: Option<String>, // "mono" | "dual"
254    pub recording_track: Option<String>,    // "inbound" | "outbound" | "both"
255}
256
257#[derive(Debug, Clone, Default)]
258pub struct RedirectAttributes {
259    pub method: Option<String>,
260}
261
262#[derive(Debug, Clone, Default)]
263pub struct ReferAttributes {
264    pub action: Option<String>,
265    pub method: Option<String>,
266}
267
268#[derive(Debug, Clone, Default)]
269pub struct RejectAttributes {
270    pub reason: Option<String>,
271}
272
273#[derive(Debug, Clone, Default)]
274pub struct SayAttributes {
275    pub language: Option<String>,
276    pub loop_count: Option<u32>,
277    pub voice: Option<String>,
278}
279
280#[derive(Debug, Clone, Default)]
281pub struct SmsAttributes {
282    pub action: Option<String>,
283    pub from: Option<String>,
284    pub method: Option<String>,
285    pub status_callback: Option<String>,
286    pub to: Option<String>,
287}
288
289#[derive(Debug, Clone, Default)]
290pub struct StartAttributes {
291    pub action: Option<String>,
292    pub method: Option<String>,
293}
294
295#[derive(Debug, Clone, Default)]
296pub struct StreamAttributes {
297    pub connector_name: Option<String>,
298    pub name: Option<String>,
299    pub status_callback: Option<String>,
300    pub status_callback_method: Option<String>,
301    pub track: Option<String>,
302    pub url: Option<String>,
303}
304
305#[derive(Debug, Clone, Default)]
306pub struct SiprecAttributes {
307    pub connector_name: Option<String>,
308    pub name: Option<String>,
309    pub status_callback: Option<String>,
310    pub status_callback_method: Option<String>,
311    pub track: Option<String>,
312}
313
314#[derive(Debug, Clone, Default)]
315pub struct TranscriptionAttributes {
316    pub enable_automatic_punctuation: Option<bool>,
317    pub hints: Option<String>,
318    pub inbound_track_label: Option<String>,
319    pub intelligence_service: Option<String>,
320    pub language_code: Option<String>,
321    pub name: Option<String>,
322    pub outbound_track_label: Option<String>,
323    pub partial_results: Option<bool>,
324    pub profanity_filter: Option<bool>,
325    pub speech_model: Option<String>,
326    pub status_callback_method: Option<String>,
327    pub status_callback_url: Option<String>,
328    pub track: Option<String>,
329    pub transcription_engine: Option<String>,
330}
331
332#[derive(Debug, Clone, Default)]
333pub struct ConfigAttributes {
334    pub name: Option<String>,
335    pub value: Option<String>,
336}
337
338#[derive(Debug, Clone, Default)]
339pub struct ParameterAttributes {
340    pub name: Option<String>,
341    pub value: Option<String>,
342}
343
344#[derive(Debug, Clone, Default)]
345pub struct RecordingAttributes {
346    pub channels: Option<String>,
347    pub recording_status_callback: Option<String>,
348    pub recording_status_callback_event: Option<Vec<String>>,
349    pub recording_status_callback_method: Option<String>,
350    pub track: Option<String>,
351    pub trim: Option<String>,
352}
353
354#[derive(Debug, Clone, Default)]
355pub struct SsmlBreakAttributes {
356    pub strength: Option<String>,
357    pub time: Option<String>,
358}
359
360#[derive(Debug, Clone, Default)]
361pub struct SsmlEmphasisAttributes {
362    pub level: Option<String>,
363}
364
365#[derive(Debug, Clone, Default)]
366pub struct SsmlLangAttributes {
367    pub xml_lang: Option<String>,
368}
369
370#[derive(Debug, Clone, Default)]
371pub struct SsmlPhonemeAttributes {
372    pub alphabet: Option<String>,
373    pub ph: Option<String>,
374}
375
376#[derive(Debug, Clone, Default)]
377pub struct SsmlProsodyAttributes {
378    pub pitch: Option<String>,
379    pub rate: Option<String>,
380    pub volume: Option<String>,
381}
382
383#[derive(Debug, Clone, Default)]
384pub struct SsmlSayAsAttributes {
385    pub format: Option<String>,
386    pub interpret_as: Option<String>,
387}
388
389#[derive(Debug, Clone, Default)]
390pub struct SsmlSubAttributes {
391    pub alias: Option<String>,
392}
393
394#[derive(Debug, Clone, Default)]
395pub struct SsmlWAttributes {
396    pub role: Option<String>,
397}
398
399// ============================================================================
400// VoiceResponse - Main Response Class
401// ============================================================================
402
403/// <Response> TwiML for Voice
404#[derive(Debug, Clone, Default)]
405pub struct VoiceResponse {
406    verbs: Vec<VoiceVerb>,
407    comments_before: Vec<String>,
408    comments: Vec<String>,
409    comments_after: Vec<String>,
410}
411
412/// Top-level TwiML verbs for voice calls
413#[derive(Debug, Clone)]
414pub enum VoiceVerb {
415    Connect(Connect),
416    Dial(Dial),
417    Echo(Echo),
418    Enqueue(Enqueue),
419    Gather(Gather),
420    Hangup(Hangup),
421    Leave(Leave),
422    Pause(Pause),
423    Pay(Pay),
424    Play(Play),
425    Prompt(Prompt),
426    Queue(Queue),
427    Record(Record),
428    Redirect(Redirect),
429    Refer(Refer),
430    Reject(Reject),
431    Say(Say),
432    Sms(Sms),
433    Start(Start),
434    Stop(Stop),
435}
436
437// ============================================================================
438// Connect Verb - Connect to other services
439// ============================================================================
440
441#[derive(Debug, Clone, Default)]
442pub struct Connect {
443    pub attributes: ConnectAttributes,
444    pub nested: Vec<ConnectNoun>,
445}
446
447impl Connect {
448    pub fn new() -> Self {
449        Self {
450            attributes: ConnectAttributes::default(),
451            nested: Vec::new(),
452        }
453    }
454
455    pub fn with_attributes(attributes: ConnectAttributes) -> Self {
456        Self {
457            attributes,
458            nested: Vec::new(),
459        }
460    }
461
462    pub fn action(mut self, action: impl Into<String>) -> Self {
463        self.attributes.action = Some(action.into());
464        self
465    }
466
467    pub fn method(mut self, method: impl Into<String>) -> Self {
468        self.attributes.method = Some(method.into());
469        self
470    }
471
472    pub fn add_stream(mut self, stream: Stream) -> Self {
473        self.nested.push(ConnectNoun::Stream(stream));
474        self
475    }
476
477    pub fn add_room(mut self, room: Room) -> Self {
478        self.nested.push(ConnectNoun::Room(room));
479        self
480    }
481
482    pub fn add_conversation(mut self, conversation: Conversation) -> Self {
483        self.nested.push(ConnectNoun::Conversation(conversation));
484        self
485    }
486
487    pub fn add_virtual_agent(mut self, agent: VirtualAgent) -> Self {
488        self.nested.push(ConnectNoun::VirtualAgent(agent));
489        self
490    }
491
492    pub fn add_autopilot(mut self, autopilot: Autopilot) -> Self {
493        self.nested.push(ConnectNoun::Autopilot(autopilot));
494        self
495    }
496
497    pub fn add_ai_session(mut self, ai_session: AiSession) -> Self {
498        self.nested.push(ConnectNoun::AiSession(ai_session));
499        self
500    }
501
502    pub fn add_conversation_relay_session(mut self, session: ConversationRelaySession) -> Self {
503        self.nested
504            .push(ConnectNoun::ConversationRelaySession(session));
505        self
506    }
507
508    pub fn add_assistant(mut self, assistant: Assistant) -> Self {
509        self.nested.push(ConnectNoun::Assistant(assistant));
510        self
511    }
512
513    pub fn add_conversation_relay(mut self, relay: ConversationRelay) -> Self {
514        self.nested.push(ConnectNoun::ConversationRelay(relay));
515        self
516    }
517}
518
519// ============================================================================
520// Dial Verb - Connect to Another Number
521// ============================================================================
522
523#[derive(Debug, Clone)]
524pub struct Dial {
525    pub attributes: DialAttributes,
526    pub number: Option<String>,
527    pub nested: Vec<DialNoun>,
528}
529
530impl Dial {
531    pub fn new() -> Self {
532        Self {
533            attributes: DialAttributes::default(),
534            number: None,
535            nested: Vec::new(),
536        }
537    }
538
539    pub fn with_attributes(attributes: DialAttributes) -> Self {
540        Self {
541            attributes,
542            number: None,
543            nested: Vec::new(),
544        }
545    }
546
547    pub fn number(mut self, number: impl Into<String>) -> Self {
548        self.number = Some(number.into());
549        self
550    }
551
552    pub fn timeout(mut self, timeout: u32) -> Self {
553        self.attributes.timeout = Some(timeout);
554        self
555    }
556
557    pub fn call_reason(mut self, call_reason: impl Into<String>) -> Self {
558        self.attributes.call_reason = Some(call_reason.into());
559        self
560    }
561
562    pub fn add_number(mut self, number: DialNumber) -> Self {
563        self.nested.push(DialNoun::Number(number));
564        self
565    }
566
567    pub fn add_client(mut self, client: DialClient) -> Self {
568        self.nested.push(DialNoun::Client(client));
569        self
570    }
571
572    pub fn add_conference(mut self, conference: DialConference) -> Self {
573        self.nested.push(DialNoun::Conference(conference));
574        self
575    }
576
577    pub fn add_queue(mut self, queue: DialQueue) -> Self {
578        self.nested.push(DialNoun::Queue(queue));
579        self
580    }
581
582    pub fn add_sip(mut self, sip: DialSip) -> Self {
583        self.nested.push(DialNoun::Sip(sip));
584        self
585    }
586
587    pub fn add_sim(mut self, sim: DialSim) -> Self {
588        self.nested.push(DialNoun::Sim(sim));
589        self
590    }
591
592    pub fn add_application(mut self, application: DialApplication) -> Self {
593        self.nested.push(DialNoun::Application(application));
594        self
595    }
596
597    pub fn add_whatsapp(mut self, whatsapp: DialWhatsApp) -> Self {
598        self.nested.push(DialNoun::WhatsApp(whatsapp));
599        self
600    }
601}
602
603// ============================================================================
604// Echo Verb
605// ============================================================================
606
607#[derive(Debug, Clone, Default)]
608pub struct Echo;
609
610impl Echo {
611    pub fn new() -> Self {
612        Self
613    }
614}
615
616// ============================================================================
617// Enqueue Verb - Add call to queue
618// ============================================================================
619
620#[derive(Debug, Clone)]
621pub struct Enqueue {
622    pub attributes: EnqueueAttributes,
623    pub name: Option<String>,
624    pub task: Option<Task>,
625}
626
627impl Enqueue {
628    pub fn new() -> Self {
629        Self {
630            attributes: EnqueueAttributes::default(),
631            name: None,
632            task: None,
633        }
634    }
635
636    pub fn name(mut self, name: impl Into<String>) -> Self {
637        self.name = Some(name.into());
638        self
639    }
640
641    pub fn action(mut self, action: impl Into<String>) -> Self {
642        self.attributes.action = Some(action.into());
643        self
644    }
645
646    pub fn method(mut self, method: impl Into<String>) -> Self {
647        self.attributes.method = Some(method.into());
648        self
649    }
650
651    pub fn wait_url(mut self, wait_url: impl Into<String>) -> Self {
652        self.attributes.wait_url = Some(wait_url.into());
653        self
654    }
655
656    pub fn wait_url_method(mut self, method: impl Into<String>) -> Self {
657        self.attributes.wait_url_method = Some(method.into());
658        self
659    }
660
661    pub fn workflow_sid(mut self, workflow_sid: impl Into<String>) -> Self {
662        self.attributes.workflow_sid = Some(workflow_sid.into());
663        self
664    }
665}
666
667// ============================================================================
668// Gather Verb - Collect User Input
669// ============================================================================
670
671#[derive(Debug, Clone)]
672pub struct Gather {
673    pub attributes: GatherAttributes,
674    pub nested: Vec<GatherNoun>,
675}
676
677impl Gather {
678    pub fn new() -> Self {
679        Self {
680            attributes: GatherAttributes::default(),
681            nested: Vec::new(),
682        }
683    }
684
685    pub fn with_attributes(attributes: GatherAttributes) -> Self {
686        Self {
687            attributes,
688            nested: Vec::new(),
689        }
690    }
691
692    pub fn input(mut self, input: Vec<String>) -> Self {
693        self.attributes.input = Some(input);
694        self
695    }
696
697    pub fn action(mut self, action: impl Into<String>) -> Self {
698        self.attributes.action = Some(action.into());
699        self
700    }
701
702    pub fn method(mut self, method: impl Into<String>) -> Self {
703        self.attributes.method = Some(method.into());
704        self
705    }
706
707    pub fn timeout(mut self, timeout: u32) -> Self {
708        self.attributes.timeout = Some(timeout);
709        self
710    }
711
712    pub fn finish_on_key(mut self, key: impl Into<String>) -> Self {
713        self.attributes.finish_on_key = Some(key.into());
714        self
715    }
716
717    pub fn num_digits(mut self, num_digits: u32) -> Self {
718        self.attributes.num_digits = Some(num_digits);
719        self
720    }
721
722    pub fn language(mut self, language: impl Into<String>) -> Self {
723        self.attributes.language = Some(language.into());
724        self
725    }
726
727    pub fn hints(mut self, hints: impl Into<String>) -> Self {
728        self.attributes.hints = Some(hints.into());
729        self
730    }
731
732    pub fn action_on_empty_result(mut self, action_on_empty_result: bool) -> Self {
733        self.attributes.action_on_empty_result = Some(action_on_empty_result);
734        self
735    }
736
737    pub fn barge_in(mut self, barge_in: bool) -> Self {
738        self.attributes.barge_in = Some(barge_in);
739        self
740    }
741
742    pub fn debug(mut self, debug: bool) -> Self {
743        self.attributes.debug = Some(debug);
744        self
745    }
746
747    pub fn dtmf_detection(mut self, dtmf_detection: bool) -> Self {
748        self.attributes.dtmf_detection = Some(dtmf_detection);
749        self
750    }
751
752    pub fn enhanced(mut self, enhanced: bool) -> Self {
753        self.attributes.enhanced = Some(enhanced);
754        self
755    }
756
757    pub fn max_speech_time(mut self, max_speech_time: u32) -> Self {
758        self.attributes.max_speech_time = Some(max_speech_time);
759        self
760    }
761
762    pub fn partial_result_callback(mut self, callback: impl Into<String>) -> Self {
763        self.attributes.partial_result_callback = Some(callback.into());
764        self
765    }
766
767    pub fn partial_result_callback_method(mut self, method: impl Into<String>) -> Self {
768        self.attributes.partial_result_callback_method = Some(method.into());
769        self
770    }
771
772    pub fn profanity_filter(mut self, profanity_filter: bool) -> Self {
773        self.attributes.profanity_filter = Some(profanity_filter);
774        self
775    }
776
777    pub fn speech_model(mut self, speech_model: impl Into<String>) -> Self {
778        self.attributes.speech_model = Some(speech_model.into());
779        self
780    }
781
782    pub fn speech_timeout(mut self, speech_timeout: impl Into<String>) -> Self {
783        self.attributes.speech_timeout = Some(speech_timeout.into());
784        self
785    }
786
787    pub fn add_say(mut self, say: Say) -> Self {
788        self.nested.push(GatherNoun::Say(say));
789        self
790    }
791
792    pub fn add_play(mut self, play: Play) -> Self {
793        self.nested.push(GatherNoun::Play(play));
794        self
795    }
796
797    pub fn add_pause(mut self, pause: Pause) -> Self {
798        self.nested.push(GatherNoun::Pause(pause));
799        self
800    }
801}
802
803// ============================================================================
804// Hangup Verb
805// ============================================================================
806
807#[derive(Debug, Clone, Default)]
808pub struct Hangup;
809
810impl Hangup {
811    pub fn new() -> Self {
812        Self
813    }
814}
815
816// ============================================================================
817// Leave Verb
818// ============================================================================
819
820#[derive(Debug, Clone, Default)]
821pub struct Leave;
822
823impl Leave {
824    pub fn new() -> Self {
825        Self
826    }
827}
828
829// ============================================================================
830// Pause Verb - Silent Pause
831// ============================================================================
832
833#[derive(Debug, Clone)]
834pub struct Pause {
835    pub attributes: PauseAttributes,
836}
837
838impl Pause {
839    pub fn new(attributes: Option<PauseAttributes>) -> Self {
840        Self {
841            attributes: attributes.unwrap_or_default(),
842        }
843    }
844}
845
846// ============================================================================
847// Pay Verb - Collect payment information
848// ============================================================================
849
850#[derive(Debug, Clone)]
851pub struct Pay {
852    pub attributes: PayAttributes,
853    pub prompts: Vec<Prompt>,
854    pub parameters: Vec<Parameter>,
855}
856
857impl Pay {
858    pub fn new() -> Self {
859        Self {
860            attributes: PayAttributes::default(),
861            prompts: Vec::new(),
862            parameters: Vec::new(),
863        }
864    }
865
866    pub fn input(mut self, input: impl Into<String>) -> Self {
867        self.attributes.input = Some(input.into());
868        self
869    }
870
871    pub fn action(mut self, action: impl Into<String>) -> Self {
872        self.attributes.action = Some(action.into());
873        self
874    }
875
876    pub fn charge_amount(mut self, charge_amount: impl Into<String>) -> Self {
877        self.attributes.charge_amount = Some(charge_amount.into());
878        self
879    }
880
881    pub fn currency(mut self, currency: impl Into<String>) -> Self {
882        self.attributes.currency = Some(currency.into());
883        self
884    }
885
886    pub fn payment_connector(mut self, payment_connector: impl Into<String>) -> Self {
887        self.attributes.payment_connector = Some(payment_connector.into());
888        self
889    }
890
891    pub fn payment_method(mut self, payment_method: impl Into<String>) -> Self {
892        self.attributes.payment_method = Some(payment_method.into());
893        self
894    }
895}
896
897// ============================================================================
898// Play Verb - Play Audio
899// ============================================================================
900
901#[derive(Debug, Clone)]
902pub struct Play {
903    pub attributes: PlayAttributes,
904    pub url: Option<String>,
905}
906
907impl Play {
908    pub fn new() -> Self {
909        Self {
910            attributes: PlayAttributes::default(),
911            url: None,
912        }
913    }
914
915    pub fn url(mut self, url: impl Into<String>) -> Self {
916        self.url = Some(url.into());
917        self
918    }
919
920    pub fn loop_count(mut self, loop_count: u32) -> Self {
921        self.attributes.loop_count = Some(loop_count);
922        self
923    }
924
925    pub fn digits(mut self, digits: impl Into<String>) -> Self {
926        self.attributes.digits = Some(digits.into());
927        self
928    }
929}
930
931// ============================================================================
932// Prompt Verb
933// ============================================================================
934
935#[derive(Debug, Clone)]
936pub struct Prompt {
937    pub attributes: PromptAttributes,
938}
939
940impl Prompt {
941    pub fn new() -> Self {
942        Self {
943            attributes: PromptAttributes::default(),
944        }
945    }
946
947    pub fn with_attributes(attributes: PromptAttributes) -> Self {
948        Self { attributes }
949    }
950
951    pub fn for_attr(mut self, for_attr: impl Into<String>) -> Self {
952        self.attributes.for_attr = Some(for_attr.into());
953        self
954    }
955
956    pub fn attempt(mut self, attempt: Vec<u32>) -> Self {
957        self.attributes.attempt = Some(attempt);
958        self
959    }
960
961    pub fn card_type(mut self, card_type: Vec<String>) -> Self {
962        self.attributes.card_type = Some(card_type);
963        self
964    }
965
966    pub fn error_type(mut self, error_type: Vec<String>) -> Self {
967        self.attributes.error_type = Some(error_type);
968        self
969    }
970}
971
972// ============================================================================
973// Queue Verb - Join a queue
974// ============================================================================
975
976#[derive(Debug, Clone)]
977pub struct Queue {
978    pub attributes: QueueAttributes,
979    pub name: String,
980}
981
982impl Queue {
983    pub fn new(attributes: QueueAttributes, name: String) -> Self {
984        Self { attributes, name }
985    }
986}
987
988// ============================================================================
989// Record Verb - Record Audio
990// ============================================================================
991
992#[derive(Debug, Clone)]
993pub struct Record {
994    pub attributes: RecordAttributes,
995}
996
997impl Record {
998    pub fn new() -> Self {
999        Self {
1000            attributes: RecordAttributes::default(),
1001        }
1002    }
1003
1004    pub fn action(mut self, action: impl Into<String>) -> Self {
1005        self.attributes.action = Some(action.into());
1006        self
1007    }
1008
1009    pub fn method(mut self, method: impl Into<String>) -> Self {
1010        self.attributes.method = Some(method.into());
1011        self
1012    }
1013
1014    pub fn timeout(mut self, timeout: u32) -> Self {
1015        self.attributes.timeout = Some(timeout);
1016        self
1017    }
1018
1019    pub fn max_length(mut self, max_length: u32) -> Self {
1020        self.attributes.max_length = Some(max_length);
1021        self
1022    }
1023
1024    pub fn play_beep(mut self, play_beep: bool) -> Self {
1025        self.attributes.play_beep = Some(play_beep);
1026        self
1027    }
1028
1029    pub fn transcribe(mut self, transcribe: bool) -> Self {
1030        self.attributes.transcribe = Some(transcribe);
1031        self
1032    }
1033
1034    pub fn transcribe_callback(mut self, callback: impl Into<String>) -> Self {
1035        self.attributes.transcribe_callback = Some(callback.into());
1036        self
1037    }
1038
1039    pub fn recording_channels(mut self, channels: impl Into<String>) -> Self {
1040        self.attributes.recording_channels = Some(channels.into());
1041        self
1042    }
1043
1044    pub fn recording_track(mut self, track: impl Into<String>) -> Self {
1045        self.attributes.recording_track = Some(track.into());
1046        self
1047    }
1048
1049    pub fn finish_on_key(mut self, key: impl Into<String>) -> Self {
1050        self.attributes.finish_on_key = Some(key.into());
1051        self
1052    }
1053
1054    pub fn trim(mut self, trim: impl Into<String>) -> Self {
1055        self.attributes.trim = Some(trim.into());
1056        self
1057    }
1058
1059    pub fn recording_status_callback(mut self, callback: impl Into<String>) -> Self {
1060        self.attributes.recording_status_callback = Some(callback.into());
1061        self
1062    }
1063
1064    pub fn recording_status_callback_method(mut self, method: impl Into<String>) -> Self {
1065        self.attributes.recording_status_callback_method = Some(method.into());
1066        self
1067    }
1068
1069    pub fn recording_status_callback_event(mut self, events: Vec<String>) -> Self {
1070        self.attributes.recording_status_callback_event = Some(events);
1071        self
1072    }
1073}
1074
1075// ============================================================================
1076// Redirect Verb
1077// ============================================================================
1078
1079#[derive(Debug, Clone)]
1080pub struct Redirect {
1081    pub attributes: RedirectAttributes,
1082    pub url: String,
1083}
1084
1085impl Redirect {
1086    pub fn new(attributes: RedirectAttributes, url: String) -> Self {
1087        Self { attributes, url }
1088    }
1089}
1090
1091// ============================================================================
1092// Refer Verb - SIP REFER
1093// ============================================================================
1094
1095#[derive(Debug, Clone)]
1096pub struct Refer {
1097    pub attributes: ReferAttributes,
1098    pub refer_sip: Option<ReferSip>,
1099}
1100
1101impl Refer {
1102    pub fn new() -> Self {
1103        Self {
1104            attributes: ReferAttributes::default(),
1105            refer_sip: None,
1106        }
1107    }
1108
1109    pub fn action(mut self, action: impl Into<String>) -> Self {
1110        self.attributes.action = Some(action.into());
1111        self
1112    }
1113
1114    pub fn method(mut self, method: impl Into<String>) -> Self {
1115        self.attributes.method = Some(method.into());
1116        self
1117    }
1118
1119    pub fn add_refer_sip(mut self, refer_sip: ReferSip) -> Self {
1120        self.refer_sip = Some(refer_sip);
1121        self
1122    }
1123}
1124
1125// ============================================================================
1126// Reject Verb
1127// ============================================================================
1128
1129#[derive(Debug, Clone)]
1130pub struct Reject {
1131    pub attributes: RejectAttributes,
1132}
1133
1134impl Reject {
1135    pub fn new() -> Self {
1136        Self {
1137            attributes: RejectAttributes::default(),
1138        }
1139    }
1140
1141    pub fn reason(mut self, reason: impl Into<String>) -> Self {
1142        self.attributes.reason = Some(reason.into());
1143        self
1144    }
1145}
1146
1147// ============================================================================
1148// Say Verb - Text-to-Speech
1149// ============================================================================
1150
1151#[derive(Debug, Clone)]
1152pub struct Say {
1153    pub attributes: SayAttributes,
1154    pub message: String,
1155    pub ssml_elements: Vec<SsmlElement>,
1156}
1157
1158impl Say {
1159    pub fn new(message: impl Into<String>) -> Self {
1160        Self {
1161            attributes: SayAttributes::default(),
1162            message: message.into(),
1163            ssml_elements: Vec::new(),
1164        }
1165    }
1166
1167    pub fn voice(mut self, voice: impl Into<String>) -> Self {
1168        self.attributes.voice = Some(voice.into());
1169        self
1170    }
1171
1172    pub fn language(mut self, language: impl Into<String>) -> Self {
1173        self.attributes.language = Some(language.into());
1174        self
1175    }
1176
1177    pub fn loop_count(mut self, loop_count: u32) -> Self {
1178        self.attributes.loop_count = Some(loop_count);
1179        self
1180    }
1181
1182    pub fn add_break(mut self, strength: Option<String>, time: Option<String>) -> Self {
1183        self.ssml_elements
1184            .push(SsmlElement::Break { strength, time });
1185        self
1186    }
1187
1188    pub fn add_emphasis(mut self, level: Option<String>, text: impl Into<String>) -> Self {
1189        self.ssml_elements.push(SsmlElement::Emphasis {
1190            level,
1191            text: text.into(),
1192        });
1193        self
1194    }
1195
1196    pub fn add_prosody(
1197        mut self,
1198        pitch: Option<String>,
1199        rate: Option<String>,
1200        volume: Option<String>,
1201        text: impl Into<String>,
1202    ) -> Self {
1203        self.ssml_elements.push(SsmlElement::Prosody {
1204            pitch,
1205            rate,
1206            volume,
1207            text: text.into(),
1208        });
1209        self
1210    }
1211
1212    pub fn add_lang(mut self, xml_lang: impl Into<String>, text: impl Into<String>) -> Self {
1213        self.ssml_elements.push(SsmlElement::Lang {
1214            xml_lang: xml_lang.into(),
1215            text: text.into(),
1216        });
1217        self
1218    }
1219
1220    pub fn add_p(mut self, text: impl Into<String>) -> Self {
1221        self.ssml_elements
1222            .push(SsmlElement::P { text: text.into() });
1223        self
1224    }
1225
1226    pub fn add_s(mut self, text: impl Into<String>) -> Self {
1227        self.ssml_elements
1228            .push(SsmlElement::S { text: text.into() });
1229        self
1230    }
1231
1232    pub fn add_phoneme(
1233        mut self,
1234        ph: impl Into<String>,
1235        text: impl Into<String>,
1236        alphabet: Option<String>,
1237    ) -> Self {
1238        self.ssml_elements.push(SsmlElement::Phoneme {
1239            alphabet,
1240            ph: ph.into(),
1241            text: text.into(),
1242        });
1243        self
1244    }
1245
1246    pub fn add_say_as(
1247        mut self,
1248        interpret_as: impl Into<String>,
1249        text: impl Into<String>,
1250        format: Option<String>,
1251    ) -> Self {
1252        self.ssml_elements.push(SsmlElement::SayAs {
1253            interpret_as: interpret_as.into(),
1254            format,
1255            text: text.into(),
1256        });
1257        self
1258    }
1259
1260    pub fn add_sub(mut self, alias: impl Into<String>, text: impl Into<String>) -> Self {
1261        self.ssml_elements.push(SsmlElement::Sub {
1262            alias: alias.into(),
1263            text: text.into(),
1264        });
1265        self
1266    }
1267
1268    pub fn add_w(mut self, text: impl Into<String>, role: Option<String>) -> Self {
1269        self.ssml_elements.push(SsmlElement::W {
1270            role,
1271            text: text.into(),
1272        });
1273        self
1274    }
1275
1276    pub fn add_amazon_effect(mut self, name: impl Into<String>, text: impl Into<String>) -> Self {
1277        self.ssml_elements.push(SsmlElement::AmazonEffect {
1278            name: name.into(),
1279            text: text.into(),
1280        });
1281        self
1282    }
1283
1284    pub fn add_amazon_domain(mut self, name: impl Into<String>, text: impl Into<String>) -> Self {
1285        self.ssml_elements.push(SsmlElement::AmazonDomain {
1286            name: name.into(),
1287            text: text.into(),
1288        });
1289        self
1290    }
1291}
1292
1293// ============================================================================
1294// Sms Verb - Send SMS during call
1295// ============================================================================
1296
1297#[derive(Debug, Clone)]
1298pub struct Sms {
1299    pub attributes: SmsAttributes,
1300    pub message: String,
1301}
1302
1303impl Sms {
1304    pub fn new(attributes: SmsAttributes, message: String) -> Self {
1305        Self {
1306            attributes,
1307            message,
1308        }
1309    }
1310}
1311
1312// ============================================================================
1313// Start Verb - Start media streaming/recording
1314// ============================================================================
1315
1316#[derive(Debug, Clone)]
1317pub struct Start {
1318    pub attributes: StartAttributes,
1319    pub nested: Vec<StartNoun>,
1320}
1321
1322impl Start {
1323    pub fn new() -> Self {
1324        Self {
1325            attributes: StartAttributes::default(),
1326            nested: Vec::new(),
1327        }
1328    }
1329
1330    pub fn action(mut self, action: impl Into<String>) -> Self {
1331        self.attributes.action = Some(action.into());
1332        self
1333    }
1334
1335    pub fn method(mut self, method: impl Into<String>) -> Self {
1336        self.attributes.method = Some(method.into());
1337        self
1338    }
1339
1340    pub fn add_stream(mut self, stream: Stream) -> Self {
1341        self.nested.push(StartNoun::Stream(stream));
1342        self
1343    }
1344
1345    pub fn add_siprec(mut self, siprec: Siprec) -> Self {
1346        self.nested.push(StartNoun::Siprec(siprec));
1347        self
1348    }
1349
1350    pub fn add_transcription(mut self, transcription: Transcription) -> Self {
1351        self.nested.push(StartNoun::Transcription(transcription));
1352        self
1353    }
1354
1355    pub fn add_recording(mut self, recording: Recording) -> Self {
1356        self.nested.push(StartNoun::Recording(recording));
1357        self
1358    }
1359}
1360
1361// ============================================================================
1362// Stop Verb
1363// ============================================================================
1364
1365#[derive(Debug, Clone, Default)]
1366pub struct Stop;
1367
1368impl Stop {
1369    pub fn new() -> Self {
1370        Self
1371    }
1372}
1373
1374// ============================================================================
1375// Supporting Types and Nested Elements
1376// ============================================================================
1377
1378// Stream - Media streaming
1379#[derive(Debug, Clone, Default)]
1380pub struct Stream {
1381    pub name: Option<String>,
1382    pub connector_name: Option<String>,
1383    pub url: Option<String>,
1384    pub track: Option<String>,
1385    pub status_callback: Option<String>,
1386    pub status_callback_method: Option<String>,
1387    pub parameters: Vec<Parameter>,
1388}
1389
1390impl Stream {
1391    pub fn new() -> Self {
1392        Self::default()
1393    }
1394
1395    pub fn name(mut self, name: impl Into<String>) -> Self {
1396        self.name = Some(name.into());
1397        self
1398    }
1399
1400    pub fn connector_name(mut self, connector_name: impl Into<String>) -> Self {
1401        self.connector_name = Some(connector_name.into());
1402        self
1403    }
1404
1405    pub fn url(mut self, url: impl Into<String>) -> Self {
1406        self.url = Some(url.into());
1407        self
1408    }
1409
1410    pub fn track(mut self, track: impl Into<String>) -> Self {
1411        self.track = Some(track.into());
1412        self
1413    }
1414
1415    pub fn status_callback(mut self, callback: impl Into<String>) -> Self {
1416        self.status_callback = Some(callback.into());
1417        self
1418    }
1419
1420    pub fn add_parameter(mut self, parameter: Parameter) -> Self {
1421        self.parameters.push(parameter);
1422        self
1423    }
1424}
1425
1426// Room - Video room
1427#[derive(Debug, Clone, Default)]
1428pub struct Room {
1429    pub name: Option<String>,
1430    pub participant_identity: Option<String>,
1431}
1432
1433impl Room {
1434    pub fn new(name: impl Into<String>) -> Self {
1435        Self {
1436            name: Some(name.into()),
1437            participant_identity: None,
1438        }
1439    }
1440
1441    pub fn participant_identity(mut self, identity: impl Into<String>) -> Self {
1442        self.participant_identity = Some(identity.into());
1443        self
1444    }
1445}
1446
1447// Conversation - Conversation API
1448#[derive(Debug, Clone, Default)]
1449pub struct Conversation {
1450    pub service_instance_sid: Option<String>,
1451}
1452
1453impl Conversation {
1454    pub fn new() -> Self {
1455        Self::default()
1456    }
1457
1458    pub fn service_instance_sid(mut self, sid: impl Into<String>) -> Self {
1459        self.service_instance_sid = Some(sid.into());
1460        self
1461    }
1462}
1463
1464// VirtualAgent - AI Virtual Agent
1465#[derive(Debug, Clone, Default)]
1466pub struct VirtualAgent {
1467    pub connector_name: Option<String>,
1468    pub language: Option<String>,
1469    pub parameters: Vec<Parameter>,
1470}
1471
1472impl VirtualAgent {
1473    pub fn new(connector_name: impl Into<String>) -> Self {
1474        Self {
1475            connector_name: Some(connector_name.into()),
1476            ..Default::default()
1477        }
1478    }
1479
1480    pub fn language(mut self, language: impl Into<String>) -> Self {
1481        self.language = Some(language.into());
1482        self
1483    }
1484
1485    pub fn add_parameter(mut self, parameter: Parameter) -> Self {
1486        self.parameters.push(parameter);
1487        self
1488    }
1489}
1490
1491// Autopilot - Autopilot Assistant
1492#[derive(Debug, Clone, Default)]
1493pub struct Autopilot {
1494    pub name: Option<String>,
1495}
1496
1497impl Autopilot {
1498    pub fn new(name: impl Into<String>) -> Self {
1499        Self {
1500            name: Some(name.into()),
1501        }
1502    }
1503}
1504
1505// AiSession - AI Session
1506#[derive(Debug, Clone, Default)]
1507pub struct AiSession {
1508    pub assistant_sid: Option<String>,
1509}
1510
1511impl AiSession {
1512    pub fn new(assistant_sid: impl Into<String>) -> Self {
1513        Self {
1514            assistant_sid: Some(assistant_sid.into()),
1515        }
1516    }
1517}
1518
1519// ConversationRelaySession - Conversation Relay Session
1520#[derive(Debug, Clone, Default)]
1521pub struct ConversationRelaySession {
1522    pub connector: Option<String>,
1523    pub session_configuration: Option<String>,
1524}
1525
1526impl ConversationRelaySession {
1527    pub fn new() -> Self {
1528        Self::default()
1529    }
1530
1531    pub fn connector(mut self, connector: impl Into<String>) -> Self {
1532        self.connector = Some(connector.into());
1533        self
1534    }
1535
1536    pub fn session_configuration(mut self, session_configuration: impl Into<String>) -> Self {
1537        self.session_configuration = Some(session_configuration.into());
1538        self
1539    }
1540}
1541
1542// Assistant - Assistant
1543#[derive(Debug, Clone, Default)]
1544pub struct Assistant {
1545    pub sid: Option<String>,
1546}
1547
1548impl Assistant {
1549    pub fn new(sid: impl Into<String>) -> Self {
1550        Self {
1551            sid: Some(sid.into()),
1552        }
1553    }
1554}
1555
1556// ConversationRelay - Conversation Relay
1557#[derive(Debug, Clone, Default)]
1558pub struct ConversationRelay {
1559    pub url: Option<String>,
1560    pub welcome_greeting: Option<String>,
1561    pub voice: Option<String>,
1562    pub language: Option<String>,
1563    pub dtmf_detection: Option<bool>,
1564    pub interruptible: Option<bool>,
1565    pub interruption_sensitivity: Option<String>,
1566    pub speech_model: Option<String>,
1567    pub profanity_filter: Option<bool>,
1568    pub transcription_enabled: Option<bool>,
1569    pub status_callback: Option<String>,
1570    pub status_callback_method: Option<String>,
1571    pub max_duration: Option<u32>,
1572    pub languages: Vec<Language>,
1573    pub parameters: Vec<Parameter>,
1574}
1575
1576impl ConversationRelay {
1577    pub fn new(url: impl Into<String>) -> Self {
1578        Self {
1579            url: Some(url.into()),
1580            ..Default::default()
1581        }
1582    }
1583
1584    pub fn url(mut self, url: impl Into<String>) -> Self {
1585        self.url = Some(url.into());
1586        self
1587    }
1588
1589    pub fn welcome_greeting(mut self, greeting: impl Into<String>) -> Self {
1590        self.welcome_greeting = Some(greeting.into());
1591        self
1592    }
1593
1594    pub fn voice(mut self, voice: impl Into<String>) -> Self {
1595        self.voice = Some(voice.into());
1596        self
1597    }
1598
1599    pub fn language(mut self, language: impl Into<String>) -> Self {
1600        self.language = Some(language.into());
1601        self
1602    }
1603
1604    pub fn dtmf_detection(mut self, enabled: bool) -> Self {
1605        self.dtmf_detection = Some(enabled);
1606        self
1607    }
1608
1609    pub fn interruptible(mut self, enabled: bool) -> Self {
1610        self.interruptible = Some(enabled);
1611        self
1612    }
1613
1614    pub fn interruption_sensitivity(mut self, sensitivity: impl Into<String>) -> Self {
1615        self.interruption_sensitivity = Some(sensitivity.into());
1616        self
1617    }
1618
1619    pub fn speech_model(mut self, model: impl Into<String>) -> Self {
1620        self.speech_model = Some(model.into());
1621        self
1622    }
1623
1624    pub fn profanity_filter(mut self, enabled: bool) -> Self {
1625        self.profanity_filter = Some(enabled);
1626        self
1627    }
1628
1629    pub fn transcription_enabled(mut self, enabled: bool) -> Self {
1630        self.transcription_enabled = Some(enabled);
1631        self
1632    }
1633
1634    pub fn status_callback(mut self, url: impl Into<String>) -> Self {
1635        self.status_callback = Some(url.into());
1636        self
1637    }
1638
1639    pub fn status_callback_method(mut self, method: impl Into<String>) -> Self {
1640        self.status_callback_method = Some(method.into());
1641        self
1642    }
1643
1644    pub fn max_duration(mut self, seconds: u32) -> Self {
1645        self.max_duration = Some(seconds);
1646        self
1647    }
1648
1649    pub fn add_language(mut self, language: Language) -> Self {
1650        self.languages.push(language);
1651        self
1652    }
1653
1654    pub fn add_parameter(mut self, parameter: Parameter) -> Self {
1655        self.parameters.push(parameter);
1656        self
1657    }
1658}
1659
1660// Parameter - Generic parameter for various verbs
1661#[derive(Debug, Clone, Default)]
1662pub struct Parameter {
1663    pub name: Option<String>,
1664    pub value: Option<String>,
1665}
1666
1667impl Parameter {
1668    pub fn new() -> Self {
1669        Self::default()
1670    }
1671
1672    pub fn name(mut self, name: impl Into<String>) -> Self {
1673        self.name = Some(name.into());
1674        self
1675    }
1676
1677    pub fn value(mut self, value: impl Into<String>) -> Self {
1678        self.value = Some(value.into());
1679        self
1680    }
1681}
1682
1683// ReferSip - SIP reference for Refer verb
1684#[derive(Debug, Clone)]
1685pub struct ReferSip {
1686    pub sip_url: String,
1687}
1688
1689impl ReferSip {
1690    pub fn new(sip_url: impl Into<String>) -> Self {
1691        Self {
1692            sip_url: sip_url.into(),
1693        }
1694    }
1695}
1696
1697// Transcription - Live transcription
1698#[derive(Debug, Clone, Default)]
1699pub struct Transcription {
1700    pub attributes: TranscriptionAttributes,
1701}
1702
1703impl Transcription {
1704    pub fn new() -> Self {
1705        Self {
1706            attributes: TranscriptionAttributes::default(),
1707        }
1708    }
1709
1710    pub fn name(mut self, name: impl Into<String>) -> Self {
1711        self.attributes.name = Some(name.into());
1712        self
1713    }
1714
1715    pub fn track(mut self, track: impl Into<String>) -> Self {
1716        self.attributes.track = Some(track.into());
1717        self
1718    }
1719
1720    pub fn language_code(mut self, language_code: impl Into<String>) -> Self {
1721        self.attributes.language_code = Some(language_code.into());
1722        self
1723    }
1724
1725    pub fn enable_automatic_punctuation(mut self, enable: bool) -> Self {
1726        self.attributes.enable_automatic_punctuation = Some(enable);
1727        self
1728    }
1729
1730    pub fn hints(mut self, hints: impl Into<String>) -> Self {
1731        self.attributes.hints = Some(hints.into());
1732        self
1733    }
1734
1735    pub fn inbound_track_label(mut self, label: impl Into<String>) -> Self {
1736        self.attributes.inbound_track_label = Some(label.into());
1737        self
1738    }
1739
1740    pub fn intelligence_service(mut self, service: impl Into<String>) -> Self {
1741        self.attributes.intelligence_service = Some(service.into());
1742        self
1743    }
1744
1745    pub fn outbound_track_label(mut self, label: impl Into<String>) -> Self {
1746        self.attributes.outbound_track_label = Some(label.into());
1747        self
1748    }
1749
1750    pub fn partial_results(mut self, enable: bool) -> Self {
1751        self.attributes.partial_results = Some(enable);
1752        self
1753    }
1754
1755    pub fn profanity_filter(mut self, enable: bool) -> Self {
1756        self.attributes.profanity_filter = Some(enable);
1757        self
1758    }
1759
1760    pub fn speech_model(mut self, model: impl Into<String>) -> Self {
1761        self.attributes.speech_model = Some(model.into());
1762        self
1763    }
1764
1765    pub fn status_callback_method(mut self, method: impl Into<String>) -> Self {
1766        self.attributes.status_callback_method = Some(method.into());
1767        self
1768    }
1769
1770    pub fn status_callback_url(mut self, url: impl Into<String>) -> Self {
1771        self.attributes.status_callback_url = Some(url.into());
1772        self
1773    }
1774
1775    pub fn transcription_engine(mut self, engine: impl Into<String>) -> Self {
1776        self.attributes.transcription_engine = Some(engine.into());
1777        self
1778    }
1779}
1780
1781// Recording - Recording configuration
1782#[derive(Debug, Clone, Default)]
1783pub struct Recording {
1784    pub recording_status_callback: Option<String>,
1785    pub recording_status_callback_method: Option<String>,
1786    pub recording_status_callback_event: Option<String>,
1787    pub trim: Option<String>,
1788    pub track: Option<String>,
1789    pub channels: Option<String>,
1790}
1791
1792impl Recording {
1793    pub fn new() -> Self {
1794        Self::default()
1795    }
1796
1797    pub fn recording_status_callback(mut self, callback: impl Into<String>) -> Self {
1798        self.recording_status_callback = Some(callback.into());
1799        self
1800    }
1801
1802    pub fn recording_status_callback_method(mut self, method: impl Into<String>) -> Self {
1803        self.recording_status_callback_method = Some(method.into());
1804        self
1805    }
1806
1807    pub fn recording_status_callback_event(mut self, event: impl Into<String>) -> Self {
1808        self.recording_status_callback_event = Some(event.into());
1809        self
1810    }
1811
1812    pub fn trim(mut self, trim: impl Into<String>) -> Self {
1813        self.trim = Some(trim.into());
1814        self
1815    }
1816
1817    pub fn track(mut self, track: impl Into<String>) -> Self {
1818        self.track = Some(track.into());
1819        self
1820    }
1821
1822    pub fn channels(mut self, channels: impl Into<String>) -> Self {
1823        self.channels = Some(channels.into());
1824        self
1825    }
1826}
1827
1828// Siprec - SIPREC recording
1829#[derive(Debug, Clone, Default)]
1830pub struct Siprec {
1831    pub name: Option<String>,
1832    pub connector_name: Option<String>,
1833    pub track: Option<String>,
1834}
1835
1836impl Siprec {
1837    pub fn new() -> Self {
1838        Self::default()
1839    }
1840
1841    pub fn name(mut self, name: impl Into<String>) -> Self {
1842        self.name = Some(name.into());
1843        self
1844    }
1845
1846    pub fn connector_name(mut self, connector_name: impl Into<String>) -> Self {
1847        self.connector_name = Some(connector_name.into());
1848        self
1849    }
1850
1851    pub fn track(mut self, track: impl Into<String>) -> Self {
1852        self.track = Some(track.into());
1853        self
1854    }
1855}
1856
1857// Task - Task for Autopilot
1858#[derive(Debug, Clone, Default)]
1859pub struct Task {
1860    pub task_sid: Option<String>,
1861}
1862
1863impl Task {
1864    pub fn new() -> Self {
1865        Self::default()
1866    }
1867
1868    pub fn task_sid(mut self, task_sid: impl Into<String>) -> Self {
1869        self.task_sid = Some(task_sid.into());
1870        self
1871    }
1872}
1873
1874// Identity - Identity for Client
1875#[derive(Debug, Clone)]
1876pub struct Identity {
1877    pub identity: String,
1878}
1879
1880impl Identity {
1881    pub fn new(identity: impl Into<String>) -> Self {
1882        Self {
1883            identity: identity.into(),
1884        }
1885    }
1886}
1887
1888// Language - Language for transcription and ConversationRelay
1889#[derive(Debug, Clone, Default)]
1890pub struct Language {
1891    pub language_code: Option<String>,
1892    pub tts_provider: Option<String>,
1893    pub stt_provider: Option<String>,
1894}
1895
1896impl Language {
1897    pub fn new(language_code: impl Into<String>) -> Self {
1898        Self {
1899            language_code: Some(language_code.into()),
1900            tts_provider: None,
1901            stt_provider: None,
1902        }
1903    }
1904
1905    pub fn tts_provider(mut self, provider: impl Into<String>) -> Self {
1906        self.tts_provider = Some(provider.into());
1907        self
1908    }
1909
1910    pub fn stt_provider(mut self, provider: impl Into<String>) -> Self {
1911        self.stt_provider = Some(provider.into());
1912        self
1913    }
1914}
1915
1916// Application - Application for Dial
1917#[derive(Debug, Clone, Default)]
1918pub struct Application {
1919    pub application_sid: Option<String>,
1920    pub customer_id: Option<String>,
1921    pub copy_parent_to: Option<String>,
1922    pub parameters: Vec<Parameter>,
1923}
1924
1925impl Application {
1926    pub fn new() -> Self {
1927        Self::default()
1928    }
1929
1930    pub fn application_sid(mut self, application_sid: impl Into<String>) -> Self {
1931        self.application_sid = Some(application_sid.into());
1932        self
1933    }
1934
1935    pub fn customer_id(mut self, customer_id: impl Into<String>) -> Self {
1936        self.customer_id = Some(customer_id.into());
1937        self
1938    }
1939
1940    pub fn copy_parent_to(mut self, copy_parent_to: impl Into<String>) -> Self {
1941        self.copy_parent_to = Some(copy_parent_to.into());
1942        self
1943    }
1944
1945    pub fn add_parameter(mut self, parameter: Parameter) -> Self {
1946        self.parameters.push(parameter);
1947        self
1948    }
1949}
1950
1951// ApplicationSid - Application SID
1952#[derive(Debug, Clone)]
1953pub struct ApplicationSid {
1954    pub sid: String,
1955}
1956
1957impl ApplicationSid {
1958    pub fn new(sid: impl Into<String>) -> Self {
1959        Self { sid: sid.into() }
1960    }
1961}
1962
1963// Config - Configuration for various services
1964#[derive(Debug, Clone, Default)]
1965pub struct Config {
1966    pub name: Option<String>,
1967    pub value: Option<String>,
1968}
1969
1970impl Config {
1971    pub fn new() -> Self {
1972        Self::default()
1973    }
1974
1975    pub fn name(mut self, name: impl Into<String>) -> Self {
1976        self.name = Some(name.into());
1977        self
1978    }
1979
1980    pub fn value(mut self, value: impl Into<String>) -> Self {
1981        self.value = Some(value.into());
1982        self
1983    }
1984}
1985
1986/// SSML elements for enhanced speech synthesis
1987#[derive(Debug, Clone)]
1988pub enum SsmlElement {
1989    Break {
1990        strength: Option<String>,
1991        time: Option<String>,
1992    },
1993    Emphasis {
1994        level: Option<String>,
1995        text: String,
1996    },
1997    Lang {
1998        xml_lang: String,
1999        text: String,
2000    },
2001    P {
2002        text: String,
2003    },
2004    Phoneme {
2005        alphabet: Option<String>,
2006        ph: String,
2007        text: String,
2008    },
2009    Prosody {
2010        pitch: Option<String>,
2011        rate: Option<String>,
2012        volume: Option<String>,
2013        text: String,
2014    },
2015    S {
2016        text: String,
2017    },
2018    SayAs {
2019        interpret_as: String,
2020        format: Option<String>,
2021        text: String,
2022    },
2023    Sub {
2024        alias: String,
2025        text: String,
2026    },
2027    W {
2028        role: Option<String>,
2029        text: String,
2030    },
2031    AmazonEffect {
2032        name: String,
2033        text: String,
2034    },
2035    AmazonDomain {
2036        name: String,
2037        text: String,
2038    },
2039}
2040
2041/// Nouns that can be nested within Dial
2042#[derive(Debug, Clone)]
2043pub enum DialNoun {
2044    Number(DialNumber),
2045    Client(DialClient),
2046    Conference(DialConference),
2047    Queue(DialQueue),
2048    Sip(DialSip),
2049    Sim(DialSim),
2050    Application(DialApplication),
2051    WhatsApp(DialWhatsApp),
2052}
2053
2054/// Nouns that can be nested within Gather
2055#[derive(Debug, Clone)]
2056pub enum GatherNoun {
2057    Say(Say),
2058    Play(Play),
2059    Pause(Pause),
2060}
2061
2062/// Nouns that can be nested within Connect
2063#[derive(Debug, Clone)]
2064pub enum ConnectNoun {
2065    Stream(Stream),
2066    Room(Room),
2067    Conversation(Conversation),
2068    VirtualAgent(VirtualAgent),
2069    Autopilot(Autopilot),
2070    AiSession(AiSession),
2071    ConversationRelaySession(ConversationRelaySession),
2072    Assistant(Assistant),
2073    ConversationRelay(ConversationRelay),
2074}
2075
2076/// Nouns that can be nested within Start
2077#[derive(Debug, Clone)]
2078pub enum StartNoun {
2079    Stream(Stream),
2080    Siprec(Siprec),
2081    Transcription(Transcription),
2082    Recording(Recording),
2083}
2084
2085// ============================================================================
2086// Dial Nested Elements
2087// ============================================================================
2088
2089#[derive(Debug, Clone)]
2090pub struct DialNumber {
2091    pub number: String,
2092    pub send_digits: Option<String>,
2093    pub url: Option<String>,
2094    pub method: Option<String>,
2095    pub status_callback: Option<String>,
2096    pub status_callback_event: Option<Vec<String>>,
2097    pub status_callback_method: Option<String>,
2098    pub call_reason: Option<String>,
2099    pub byoc: Option<String>,
2100    pub machine_detection: Option<String>,
2101    pub machine_detection_timeout: Option<u32>,
2102    pub machine_detection_speech_threshold: Option<u32>,
2103    pub machine_detection_speech_end_threshold: Option<u32>,
2104    pub machine_detection_silence_timeout: Option<u32>,
2105    pub amd_status_callback: Option<String>,
2106    pub amd_status_callback_method: Option<String>,
2107}
2108
2109impl DialNumber {
2110    pub fn new(number: impl Into<String>) -> Self {
2111        Self {
2112            number: number.into(),
2113            send_digits: None,
2114            url: None,
2115            method: None,
2116            status_callback: None,
2117            status_callback_event: None,
2118            status_callback_method: None,
2119            call_reason: None,
2120            byoc: None,
2121            machine_detection: None,
2122            machine_detection_timeout: None,
2123            machine_detection_speech_threshold: None,
2124            machine_detection_speech_end_threshold: None,
2125            machine_detection_silence_timeout: None,
2126            amd_status_callback: None,
2127            amd_status_callback_method: None,
2128        }
2129    }
2130
2131    pub fn send_digits(mut self, digits: impl Into<String>) -> Self {
2132        self.send_digits = Some(digits.into());
2133        self
2134    }
2135
2136    pub fn url(mut self, url: impl Into<String>) -> Self {
2137        self.url = Some(url.into());
2138        self
2139    }
2140
2141    pub fn method(mut self, method: impl Into<String>) -> Self {
2142        self.method = Some(method.into());
2143        self
2144    }
2145
2146    pub fn status_callback(mut self, callback: impl Into<String>) -> Self {
2147        self.status_callback = Some(callback.into());
2148        self
2149    }
2150
2151    pub fn status_callback_event(mut self, events: Vec<String>) -> Self {
2152        self.status_callback_event = Some(events);
2153        self
2154    }
2155
2156    pub fn status_callback_method(mut self, method: impl Into<String>) -> Self {
2157        self.status_callback_method = Some(method.into());
2158        self
2159    }
2160
2161    pub fn call_reason(mut self, call_reason: impl Into<String>) -> Self {
2162        self.call_reason = Some(call_reason.into());
2163        self
2164    }
2165
2166    pub fn byoc(mut self, byoc: impl Into<String>) -> Self {
2167        self.byoc = Some(byoc.into());
2168        self
2169    }
2170
2171    pub fn machine_detection(mut self, machine_detection: impl Into<String>) -> Self {
2172        self.machine_detection = Some(machine_detection.into());
2173        self
2174    }
2175
2176    pub fn machine_detection_timeout(mut self, timeout: u32) -> Self {
2177        self.machine_detection_timeout = Some(timeout);
2178        self
2179    }
2180
2181    pub fn machine_detection_speech_threshold(mut self, threshold: u32) -> Self {
2182        self.machine_detection_speech_threshold = Some(threshold);
2183        self
2184    }
2185
2186    pub fn machine_detection_speech_end_threshold(mut self, threshold: u32) -> Self {
2187        self.machine_detection_speech_end_threshold = Some(threshold);
2188        self
2189    }
2190
2191    pub fn machine_detection_silence_timeout(mut self, timeout: u32) -> Self {
2192        self.machine_detection_silence_timeout = Some(timeout);
2193        self
2194    }
2195
2196    pub fn amd_status_callback(mut self, callback: impl Into<String>) -> Self {
2197        self.amd_status_callback = Some(callback.into());
2198        self
2199    }
2200
2201    pub fn amd_status_callback_method(mut self, method: impl Into<String>) -> Self {
2202        self.amd_status_callback_method = Some(method.into());
2203        self
2204    }
2205}
2206
2207#[derive(Debug, Clone)]
2208pub struct DialClient {
2209    pub identity: String,
2210    pub url: Option<String>,
2211    pub method: Option<String>,
2212    pub status_callback_event: Option<Vec<String>>,
2213    pub status_callback: Option<String>,
2214    pub status_callback_method: Option<String>,
2215    pub client_notification_url: Option<String>,
2216}
2217
2218impl DialClient {
2219    pub fn new(identity: impl Into<String>) -> Self {
2220        Self {
2221            identity: identity.into(),
2222            url: None,
2223            method: None,
2224            status_callback_event: None,
2225            status_callback: None,
2226            status_callback_method: None,
2227            client_notification_url: None,
2228        }
2229    }
2230
2231    pub fn url(mut self, url: impl Into<String>) -> Self {
2232        self.url = Some(url.into());
2233        self
2234    }
2235
2236    pub fn method(mut self, method: impl Into<String>) -> Self {
2237        self.method = Some(method.into());
2238        self
2239    }
2240
2241    pub fn status_callback_event(mut self, events: Vec<String>) -> Self {
2242        self.status_callback_event = Some(events);
2243        self
2244    }
2245
2246    pub fn status_callback(mut self, callback: impl Into<String>) -> Self {
2247        self.status_callback = Some(callback.into());
2248        self
2249    }
2250
2251    pub fn status_callback_method(mut self, method: impl Into<String>) -> Self {
2252        self.status_callback_method = Some(method.into());
2253        self
2254    }
2255
2256    pub fn client_notification_url(mut self, url: impl Into<String>) -> Self {
2257        self.client_notification_url = Some(url.into());
2258        self
2259    }
2260}
2261
2262#[derive(Debug, Clone)]
2263pub struct DialConference {
2264    pub name: String,
2265    pub muted: Option<bool>,
2266    pub beep: Option<String>,
2267    pub start_conference_on_enter: Option<bool>,
2268    pub end_conference_on_exit: Option<bool>,
2269    pub wait_url: Option<String>,
2270    pub wait_method: Option<String>,
2271    pub max_participants: Option<u32>,
2272    pub record: Option<String>,
2273    pub region: Option<String>,
2274    pub coach: Option<String>,
2275    pub trim: Option<String>,
2276    pub status_callback_event: Option<Vec<String>>,
2277    pub status_callback: Option<String>,
2278    pub status_callback_method: Option<String>,
2279    pub recording_status_callback: Option<String>,
2280    pub recording_status_callback_method: Option<String>,
2281    pub recording_status_callback_event: Option<Vec<String>>,
2282    pub event_callback_url: Option<String>,
2283    pub jitter_buffer_size: Option<String>,
2284    pub participant_label: Option<String>,
2285    pub call_sid_to_coach: Option<String>,
2286    pub beep_on_customer_entrance: Option<bool>,
2287    pub coaching: Option<bool>,
2288}
2289
2290impl DialConference {
2291    pub fn new(name: impl Into<String>) -> Self {
2292        Self {
2293            name: name.into(),
2294            muted: None,
2295            beep: None,
2296            start_conference_on_enter: None,
2297            end_conference_on_exit: None,
2298            wait_url: None,
2299            wait_method: None,
2300            max_participants: None,
2301            record: None,
2302            region: None,
2303            coach: None,
2304            trim: None,
2305            status_callback_event: None,
2306            status_callback: None,
2307            status_callback_method: None,
2308            recording_status_callback: None,
2309            recording_status_callback_method: None,
2310            recording_status_callback_event: None,
2311            event_callback_url: None,
2312            jitter_buffer_size: None,
2313            participant_label: None,
2314            call_sid_to_coach: None,
2315            beep_on_customer_entrance: None,
2316            coaching: None,
2317        }
2318    }
2319
2320    pub fn muted(mut self, muted: bool) -> Self {
2321        self.muted = Some(muted);
2322        self
2323    }
2324
2325    pub fn beep(mut self, beep: impl Into<String>) -> Self {
2326        self.beep = Some(beep.into());
2327        self
2328    }
2329
2330    pub fn start_conference_on_enter(mut self, start: bool) -> Self {
2331        self.start_conference_on_enter = Some(start);
2332        self
2333    }
2334
2335    pub fn end_conference_on_exit(mut self, end: bool) -> Self {
2336        self.end_conference_on_exit = Some(end);
2337        self
2338    }
2339
2340    pub fn max_participants(mut self, max: u32) -> Self {
2341        self.max_participants = Some(max);
2342        self
2343    }
2344
2345    pub fn record(mut self, record: impl Into<String>) -> Self {
2346        self.record = Some(record.into());
2347        self
2348    }
2349
2350    pub fn wait_url(mut self, url: impl Into<String>) -> Self {
2351        self.wait_url = Some(url.into());
2352        self
2353    }
2354
2355    pub fn wait_method(mut self, method: impl Into<String>) -> Self {
2356        self.wait_method = Some(method.into());
2357        self
2358    }
2359
2360    pub fn region(mut self, region: impl Into<String>) -> Self {
2361        self.region = Some(region.into());
2362        self
2363    }
2364
2365    pub fn coach(mut self, coach: impl Into<String>) -> Self {
2366        self.coach = Some(coach.into());
2367        self
2368    }
2369
2370    pub fn trim(mut self, trim: impl Into<String>) -> Self {
2371        self.trim = Some(trim.into());
2372        self
2373    }
2374
2375    pub fn status_callback_event(mut self, events: Vec<String>) -> Self {
2376        self.status_callback_event = Some(events);
2377        self
2378    }
2379
2380    pub fn status_callback(mut self, callback: impl Into<String>) -> Self {
2381        self.status_callback = Some(callback.into());
2382        self
2383    }
2384
2385    pub fn status_callback_method(mut self, method: impl Into<String>) -> Self {
2386        self.status_callback_method = Some(method.into());
2387        self
2388    }
2389
2390    pub fn recording_status_callback(mut self, callback: impl Into<String>) -> Self {
2391        self.recording_status_callback = Some(callback.into());
2392        self
2393    }
2394
2395    pub fn recording_status_callback_method(mut self, method: impl Into<String>) -> Self {
2396        self.recording_status_callback_method = Some(method.into());
2397        self
2398    }
2399
2400    pub fn recording_status_callback_event(mut self, events: Vec<String>) -> Self {
2401        self.recording_status_callback_event = Some(events);
2402        self
2403    }
2404
2405    pub fn event_callback_url(mut self, url: impl Into<String>) -> Self {
2406        self.event_callback_url = Some(url.into());
2407        self
2408    }
2409
2410    pub fn jitter_buffer_size(mut self, size: impl Into<String>) -> Self {
2411        self.jitter_buffer_size = Some(size.into());
2412        self
2413    }
2414
2415    pub fn participant_label(mut self, label: impl Into<String>) -> Self {
2416        self.participant_label = Some(label.into());
2417        self
2418    }
2419
2420    pub fn call_sid_to_coach(mut self, call_sid: impl Into<String>) -> Self {
2421        self.call_sid_to_coach = Some(call_sid.into());
2422        self
2423    }
2424
2425    pub fn beep_on_customer_entrance(mut self, beep: bool) -> Self {
2426        self.beep_on_customer_entrance = Some(beep);
2427        self
2428    }
2429
2430    pub fn coaching(mut self, coaching: bool) -> Self {
2431        self.coaching = Some(coaching);
2432        self
2433    }
2434}
2435
2436#[derive(Debug, Clone)]
2437pub struct DialQueue {
2438    pub name: String,
2439    pub url: Option<String>,
2440    pub method: Option<String>,
2441    pub reservation_sid: Option<String>,
2442    pub post_work_activity_sid: Option<String>,
2443}
2444
2445impl DialQueue {
2446    pub fn new(name: impl Into<String>) -> Self {
2447        Self {
2448            name: name.into(),
2449            url: None,
2450            method: None,
2451            reservation_sid: None,
2452            post_work_activity_sid: None,
2453        }
2454    }
2455
2456    pub fn url(mut self, url: impl Into<String>) -> Self {
2457        self.url = Some(url.into());
2458        self
2459    }
2460
2461    pub fn method(mut self, method: impl Into<String>) -> Self {
2462        self.method = Some(method.into());
2463        self
2464    }
2465}
2466
2467#[derive(Debug, Clone)]
2468pub struct DialSip {
2469    pub sip_url: String,
2470    pub url: Option<String>,
2471    pub method: Option<String>,
2472    pub username: Option<String>,
2473    pub password: Option<String>,
2474    pub status_callback_event: Option<Vec<String>>,
2475    pub status_callback: Option<String>,
2476    pub status_callback_method: Option<String>,
2477    pub codecs: Option<Vec<String>>,
2478    pub custom_headers: Option<Vec<(String, String)>>,
2479}
2480
2481impl DialSip {
2482    pub fn new(sip_url: impl Into<String>) -> Self {
2483        Self {
2484            sip_url: sip_url.into(),
2485            url: None,
2486            method: None,
2487            username: None,
2488            password: None,
2489            status_callback_event: None,
2490            status_callback: None,
2491            status_callback_method: None,
2492            codecs: None,
2493            custom_headers: None,
2494        }
2495    }
2496
2497    pub fn url(mut self, url: impl Into<String>) -> Self {
2498        self.url = Some(url.into());
2499        self
2500    }
2501
2502    pub fn method(mut self, method: impl Into<String>) -> Self {
2503        self.method = Some(method.into());
2504        self
2505    }
2506
2507    pub fn username(mut self, username: impl Into<String>) -> Self {
2508        self.username = Some(username.into());
2509        self
2510    }
2511
2512    pub fn password(mut self, password: impl Into<String>) -> Self {
2513        self.password = Some(password.into());
2514        self
2515    }
2516
2517    pub fn status_callback_event(mut self, events: Vec<String>) -> Self {
2518        self.status_callback_event = Some(events);
2519        self
2520    }
2521
2522    pub fn status_callback(mut self, callback: impl Into<String>) -> Self {
2523        self.status_callback = Some(callback.into());
2524        self
2525    }
2526
2527    pub fn status_callback_method(mut self, method: impl Into<String>) -> Self {
2528        self.status_callback_method = Some(method.into());
2529        self
2530    }
2531
2532    pub fn codecs(mut self, codecs: Vec<String>) -> Self {
2533        self.codecs = Some(codecs);
2534        self
2535    }
2536
2537    pub fn add_codec(mut self, codec: impl Into<String>) -> Self {
2538        if let Some(ref mut codecs) = self.codecs {
2539            codecs.push(codec.into());
2540        } else {
2541            self.codecs = Some(vec![codec.into()]);
2542        }
2543        self
2544    }
2545
2546    pub fn custom_headers(mut self, headers: Vec<(String, String)>) -> Self {
2547        self.custom_headers = Some(headers);
2548        self
2549    }
2550
2551    pub fn add_custom_header(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
2552        if let Some(ref mut headers) = self.custom_headers {
2553            headers.push((name.into(), value.into()));
2554        } else {
2555            self.custom_headers = Some(vec![(name.into(), value.into())]);
2556        }
2557        self
2558    }
2559}
2560
2561#[derive(Debug, Clone)]
2562pub struct DialSim {
2563    pub sim_sid: String,
2564}
2565
2566impl DialSim {
2567    pub fn new(sim_sid: impl Into<String>) -> Self {
2568        Self {
2569            sim_sid: sim_sid.into(),
2570        }
2571    }
2572}
2573
2574#[derive(Debug, Clone, Default)]
2575pub struct DialApplication {
2576    pub application_sid: Option<String>,
2577    pub customer_id: Option<String>,
2578    pub copy_parent_to: Option<String>,
2579    pub parameters: Vec<Parameter>,
2580}
2581
2582impl DialApplication {
2583    pub fn new(application_sid: impl Into<String>) -> Self {
2584        Self {
2585            application_sid: Some(application_sid.into()),
2586            customer_id: None,
2587            copy_parent_to: None,
2588            parameters: Vec::new(),
2589        }
2590    }
2591
2592    pub fn customer_id(mut self, customer_id: impl Into<String>) -> Self {
2593        self.customer_id = Some(customer_id.into());
2594        self
2595    }
2596
2597    pub fn copy_parent_to(mut self, copy_parent_to: impl Into<String>) -> Self {
2598        self.copy_parent_to = Some(copy_parent_to.into());
2599        self
2600    }
2601
2602    pub fn add_parameter(mut self, parameter: Parameter) -> Self {
2603        self.parameters.push(parameter);
2604        self
2605    }
2606}
2607
2608#[derive(Debug, Clone)]
2609pub struct DialWhatsApp {
2610    pub phone_number: String,
2611    pub url: Option<String>,
2612    pub method: Option<String>,
2613    pub status_callback_event: Option<Vec<String>>,
2614    pub status_callback: Option<String>,
2615    pub status_callback_method: Option<String>,
2616}
2617
2618impl DialWhatsApp {
2619    pub fn new(phone_number: impl Into<String>) -> Self {
2620        Self {
2621            phone_number: phone_number.into(),
2622            url: None,
2623            method: None,
2624            status_callback_event: None,
2625            status_callback: None,
2626            status_callback_method: None,
2627        }
2628    }
2629
2630    pub fn url(mut self, url: impl Into<String>) -> Self {
2631        self.url = Some(url.into());
2632        self
2633    }
2634
2635    pub fn method(mut self, method: impl Into<String>) -> Self {
2636        self.method = Some(method.into());
2637        self
2638    }
2639}
2640
2641impl VoiceResponse {
2642    /// <Response> TwiML for Voice
2643    pub fn new() -> Self {
2644        Self::default()
2645    }
2646
2647    /// Comments in <Response>
2648    ///
2649    /// # Arguments
2650    /// * `comment` - XML Comment
2651    pub fn comment(mut self, comment: impl Into<String>) -> Self {
2652        self.comments.push(comment.into());
2653        self
2654    }
2655
2656    /// Comments after <Response>
2657    ///
2658    /// # Arguments
2659    /// * `comment` - XML Comment
2660    pub fn comment_after(mut self, comment: impl Into<String>) -> Self {
2661        self.comments_after.push(comment.into());
2662        self
2663    }
2664
2665    /// Comments before <Response>
2666    ///
2667    /// # Arguments
2668    /// * `comment` - XML Comment
2669    pub fn comment_before(mut self, comment: impl Into<String>) -> Self {
2670        self.comments_before.push(comment.into());
2671        self
2672    }
2673
2674    /// <Connect> TwiML Verb
2675    ///
2676    /// # Arguments
2677    /// * `connect` - Pre-configured Connect object
2678    pub fn connect(mut self, connect: Connect) -> Self {
2679        self.verbs.push(VoiceVerb::Connect(connect));
2680        self
2681    }
2682
2683    /// <Dial> TwiML Verb
2684    ///
2685    /// # Arguments
2686    /// * `number` - Phone number to dial
2687    pub fn dial(mut self, number: impl Into<String>) -> Self {
2688        let dial = Dial::new().number(number);
2689        self.verbs.push(VoiceVerb::Dial(dial));
2690        self
2691    }
2692
2693    /// <Dial> TwiML Verb with attributes
2694    ///
2695    /// # Arguments
2696    /// * `attributes` - TwiML attributes
2697    /// * `number` - Phone number to dial (optional)
2698    pub fn dial_with_attributes(
2699        mut self,
2700        attributes: DialAttributes,
2701        number: Option<impl Into<String>>,
2702    ) -> Dial {
2703        let mut dial = Dial::with_attributes(attributes);
2704        if let Some(n) = number {
2705            dial = dial.number(n);
2706        }
2707        self.verbs.push(VoiceVerb::Dial(dial.clone()));
2708        dial
2709    }
2710
2711    /// <Echo> TwiML Verb
2712    pub fn echo(mut self) -> Self {
2713        let echo = Echo::new();
2714        self.verbs.push(VoiceVerb::Echo(echo));
2715        self
2716    }
2717
2718    /// <Enqueue> TwiML Noun
2719    ///
2720    /// # Arguments
2721    /// * `enqueue` - Pre-configured Enqueue object
2722    pub fn enqueue(mut self, enqueue: Enqueue) -> Self {
2723        self.verbs.push(VoiceVerb::Enqueue(enqueue));
2724        self
2725    }
2726
2727    /// <Gather> TwiML Verb
2728    ///
2729    /// # Arguments
2730    /// * `gather` - Pre-configured Gather object
2731    pub fn gather(mut self, gather: Gather) -> Self {
2732        self.verbs.push(VoiceVerb::Gather(gather));
2733        self
2734    }
2735
2736    /// <Hangup> TwiML Verb
2737    pub fn hangup(mut self) -> Self {
2738        let hangup = Hangup::new();
2739        self.verbs.push(VoiceVerb::Hangup(hangup));
2740        self
2741    }
2742
2743    /// <Leave> TwiML Verb
2744    pub fn leave(mut self) -> Self {
2745        let leave = Leave::new();
2746        self.verbs.push(VoiceVerb::Leave(leave));
2747        self
2748    }
2749
2750    /// <Pause> TwiML Verb
2751    ///
2752    /// # Arguments
2753    /// * `length` - Pause length in seconds (optional)
2754    pub fn pause(mut self, length: Option<u32>) -> Self {
2755        let pause = Pause::new(length.map(|l| PauseAttributes { length: Some(l) }));
2756        self.verbs.push(VoiceVerb::Pause(pause));
2757        self
2758    }
2759
2760    /// <Pay> TwiML Verb
2761    ///
2762    /// # Arguments
2763    /// * `pay` - Pre-configured Pay object
2764    pub fn pay(mut self, pay: Pay) -> Self {
2765        self.verbs.push(VoiceVerb::Pay(pay));
2766        self
2767    }
2768
2769    /// <Play> TwiML Verb
2770    ///
2771    /// # Arguments
2772    /// * `url` - Media URL
2773    pub fn play(mut self, url: impl Into<String>) -> Self {
2774        let play = Play::new().url(url);
2775        self.verbs.push(VoiceVerb::Play(play));
2776        self
2777    }
2778
2779    /// <Record> TwiML Verb
2780    ///
2781    /// # Arguments
2782    /// * `record` - Pre-configured Record object
2783    pub fn record(mut self, record: Record) -> Self {
2784        self.verbs.push(VoiceVerb::Record(record));
2785        self
2786    }
2787
2788    /// <Redirect> TwiML Verb
2789    ///
2790    /// # Arguments
2791    /// * `url` - Redirect URL
2792    pub fn redirect(mut self, url: impl Into<String>) -> Self {
2793        let redirect = Redirect::new(RedirectAttributes::default(), url.into());
2794        self.verbs.push(VoiceVerb::Redirect(redirect));
2795        self
2796    }
2797
2798    /// <Refer> TwiML Verb
2799    ///
2800    /// # Arguments
2801    /// * `refer` - Pre-configured Refer object
2802    pub fn refer(mut self, refer: Refer) -> Self {
2803        self.verbs.push(VoiceVerb::Refer(refer));
2804        self
2805    }
2806
2807    /// <Reject> TwiML Verb
2808    ///
2809    /// # Arguments
2810    /// * `reject` - Pre-configured Reject object
2811    pub fn reject(mut self, reject: Reject) -> Self {
2812        self.verbs.push(VoiceVerb::Reject(reject));
2813        self
2814    }
2815
2816    /// <Say> TwiML Verb
2817    ///
2818    /// # Arguments
2819    /// * `message` - Message to say
2820    pub fn say(mut self, message: impl Into<String>) -> Self {
2821        let say = Say::new(message);
2822        self.verbs.push(VoiceVerb::Say(say));
2823        self
2824    }
2825
2826    /// <Start> TwiML Verb
2827    ///
2828    /// # Arguments
2829    /// * `start` - Pre-configured Start object
2830    pub fn start(mut self, start: Start) -> Self {
2831        self.verbs.push(VoiceVerb::Start(start));
2832        self
2833    }
2834
2835    /// <Stop> TwiML Verb
2836    pub fn stop(mut self) -> Self {
2837        let stop = Stop::new();
2838        self.verbs.push(VoiceVerb::Stop(stop));
2839        self
2840    }
2841
2842    /// <Sms> TwiML Noun
2843    ///
2844    /// # Arguments
2845    /// * `message` - SMS message body
2846    pub fn sms(mut self, message: impl Into<String>) -> Self {
2847        let sms = Sms::new(SmsAttributes::default(), message.into());
2848        self.verbs.push(VoiceVerb::Sms(sms));
2849        self
2850    }
2851
2852    /// <Queue> TwiML Noun
2853    ///
2854    /// # Arguments
2855    /// * `name` - Queue name
2856    pub fn queue(mut self, name: impl Into<String>) -> Self {
2857        let queue = Queue::new(QueueAttributes::default(), name.into());
2858        self.verbs.push(VoiceVerb::Queue(queue));
2859        self
2860    }
2861
2862    /// <Prompt> TwiML Verb
2863    ///
2864    /// # Arguments
2865    /// * `prompt` - Pre-configured Prompt object
2866    pub fn prompt(mut self, prompt: Prompt) -> Self {
2867        self.verbs.push(VoiceVerb::Prompt(prompt));
2868        self
2869    }
2870
2871    // ========================================================================
2872    // Convenience methods for simpler API
2873    // ========================================================================
2874
2875    /// Add a Say verb with a pre-configured Say object
2876    pub fn say_with(mut self, say: Say) -> Self {
2877        self.verbs.push(VoiceVerb::Say(say));
2878        self
2879    }
2880
2881    /// Add a Play verb with a pre-configured Play object
2882    pub fn play_with(mut self, play: Play) -> Self {
2883        self.verbs.push(VoiceVerb::Play(play));
2884        self
2885    }
2886
2887    /// Add a Dial verb with a pre-configured Dial object
2888    pub fn dial_with(mut self, dial: Dial) -> Self {
2889        self.verbs.push(VoiceVerb::Dial(dial));
2890        self
2891    }
2892
2893    /// Add a Gather verb with a pre-configured Gather object
2894    pub fn gather_with(mut self, gather: Gather) -> Self {
2895        self.verbs.push(VoiceVerb::Gather(gather));
2896        self
2897    }
2898
2899    /// Add a Record verb with a pre-configured Record object
2900    pub fn record_with(mut self, record: Record) -> Self {
2901        self.verbs.push(VoiceVerb::Record(record));
2902        self
2903    }
2904
2905    /// Add a Connect verb with a pre-configured Connect object
2906    pub fn connect_with(mut self, connect: Connect) -> Self {
2907        self.verbs.push(VoiceVerb::Connect(connect));
2908        self
2909    }
2910
2911    /// Add an Enqueue verb with a pre-configured Enqueue object
2912    pub fn enqueue_with(mut self, enqueue: Enqueue) -> Self {
2913        self.verbs.push(VoiceVerb::Enqueue(enqueue));
2914        self
2915    }
2916
2917    /// Add a Pay verb with a pre-configured Pay object
2918    pub fn pay_with(mut self, pay: Pay) -> Self {
2919        self.verbs.push(VoiceVerb::Pay(pay));
2920        self
2921    }
2922
2923    /// Add a Refer verb with a pre-configured Refer object
2924    pub fn refer_with(mut self, refer: Refer) -> Self {
2925        self.verbs.push(VoiceVerb::Refer(refer));
2926        self
2927    }
2928
2929    /// Add a Reject verb with a pre-configured Reject object
2930    pub fn reject_with(mut self, reject: Reject) -> Self {
2931        self.verbs.push(VoiceVerb::Reject(reject));
2932        self
2933    }
2934
2935    /// Add a Start verb with a pre-configured Start object
2936    pub fn start_with(mut self, start: Start) -> Self {
2937        self.verbs.push(VoiceVerb::Start(start));
2938        self
2939    }
2940
2941    /// Simple pause with just a length parameter
2942    pub fn pause_simple(mut self, length: u32) -> Self {
2943        let pause = Pause::new(Some(PauseAttributes {
2944            length: Some(length),
2945        }));
2946        self.verbs.push(VoiceVerb::Pause(pause));
2947        self
2948    }
2949
2950    /// Add an Sms verb with a pre-configured Sms object
2951    pub fn sms_with(mut self, sms: Sms) -> Self {
2952        self.verbs.push(VoiceVerb::Sms(sms));
2953        self
2954    }
2955
2956    /// Add a Queue verb with a pre-configured Queue object
2957    pub fn queue_with(mut self, queue: Queue) -> Self {
2958        self.verbs.push(VoiceVerb::Queue(queue));
2959        self
2960    }
2961
2962    /// Add a Prompt verb with a pre-configured Prompt object
2963    pub fn prompt_with(mut self, prompt: Prompt) -> Self {
2964        self.verbs.push(VoiceVerb::Prompt(prompt));
2965        self
2966    }
2967}
2968
2969impl TwiML for VoiceResponse {
2970    fn to_xml(&self) -> String {
2971        let mut xml = String::from("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
2972
2973        // Add comments before Response
2974        for comment in &self.comments_before {
2975            xml.push_str(&format!("<!-- {} -->\n", escape_xml_text(comment)));
2976        }
2977
2978        xml.push_str("<Response>\n");
2979
2980        // Add comments inside Response
2981        for comment in &self.comments {
2982            xml.push_str(&format!("  <!-- {} -->\n", escape_xml_text(comment)));
2983        }
2984
2985        for verb in &self.verbs {
2986            match verb {
2987                VoiceVerb::Say(say) => {
2988                    xml.push_str("  <Say");
2989                    if let Some(v) = &say.attributes.voice {
2990                        xml.push_str(&format!(" voice=\"{}\"", escape_xml_attr(v)));
2991                    }
2992                    if let Some(l) = &say.attributes.language {
2993                        xml.push_str(&format!(" language=\"{}\"", escape_xml_attr(l)));
2994                    }
2995                    if let Some(lc) = say.attributes.loop_count {
2996                        xml.push_str(&format!(" loop=\"{}\"", lc));
2997                    }
2998                    xml.push_str(">");
2999
3000                    // Add message
3001                    xml.push_str(&escape_xml_text(&say.message));
3002
3003                    // Add SSML elements
3004                    for ssml in &say.ssml_elements {
3005                        match ssml {
3006                            SsmlElement::Break { strength, time } => {
3007                                xml.push_str("<break");
3008                                if let Some(s) = strength {
3009                                    xml.push_str(&format!(" strength=\"{}\"", escape_xml_attr(s)));
3010                                }
3011                                if let Some(t) = time {
3012                                    xml.push_str(&format!(" time=\"{}\"", escape_xml_attr(t)));
3013                                }
3014                                xml.push_str(" />");
3015                            }
3016                            SsmlElement::Emphasis { level, text } => {
3017                                xml.push_str("<emphasis");
3018                                if let Some(l) = level {
3019                                    xml.push_str(&format!(" level=\"{}\"", escape_xml_attr(l)));
3020                                }
3021                                xml.push_str(&format!(">{}</emphasis>", escape_xml_text(text)));
3022                            }
3023                            SsmlElement::Prosody {
3024                                pitch,
3025                                rate,
3026                                volume,
3027                                text,
3028                            } => {
3029                                xml.push_str("<prosody");
3030                                if let Some(p) = pitch {
3031                                    xml.push_str(&format!(" pitch=\"{}\"", escape_xml_attr(p)));
3032                                }
3033                                if let Some(r) = rate {
3034                                    xml.push_str(&format!(" rate=\"{}\"", escape_xml_attr(r)));
3035                                }
3036                                if let Some(v) = volume {
3037                                    xml.push_str(&format!(" volume=\"{}\"", escape_xml_attr(v)));
3038                                }
3039                                xml.push_str(&format!(">{}</prosody>", escape_xml_text(text)));
3040                            }
3041                            SsmlElement::SayAs {
3042                                interpret_as,
3043                                format,
3044                                text,
3045                            } => {
3046                                xml.push_str(&format!(
3047                                    "<say-as interpret-as=\"{}\"",
3048                                    escape_xml_attr(interpret_as)
3049                                ));
3050                                if let Some(f) = format {
3051                                    xml.push_str(&format!(" format=\"{}\"", escape_xml_attr(f)));
3052                                }
3053                                xml.push_str(&format!(">{}</say-as>", escape_xml_text(text)));
3054                            }
3055                            SsmlElement::Sub { alias, text } => {
3056                                xml.push_str(&format!(
3057                                    "<sub alias=\"{}\">{}</sub>",
3058                                    escape_xml_attr(alias),
3059                                    escape_xml_text(text)
3060                                ));
3061                            }
3062                            SsmlElement::P { text } => {
3063                                xml.push_str(&format!("<p>{}</p>", escape_xml_text(text)));
3064                            }
3065                            SsmlElement::S { text } => {
3066                                xml.push_str(&format!("<s>{}</s>", escape_xml_text(text)));
3067                            }
3068                            SsmlElement::Lang { xml_lang, text } => {
3069                                xml.push_str(&format!(
3070                                    "<lang xml:lang=\"{}\">{}</lang>",
3071                                    escape_xml_attr(xml_lang),
3072                                    escape_xml_text(text)
3073                                ));
3074                            }
3075                            SsmlElement::Phoneme { alphabet, ph, text } => {
3076                                xml.push_str("<phoneme");
3077                                if let Some(a) = alphabet {
3078                                    xml.push_str(&format!(" alphabet=\"{}\"", escape_xml_attr(a)));
3079                                }
3080                                xml.push_str(&format!(
3081                                    " ph=\"{}\">{}</phoneme>",
3082                                    escape_xml_attr(ph),
3083                                    escape_xml_text(text)
3084                                ));
3085                            }
3086                            SsmlElement::W { role, text } => {
3087                                xml.push_str("<w");
3088                                if let Some(r) = role {
3089                                    xml.push_str(&format!(" role=\"{}\"", escape_xml_attr(r)));
3090                                }
3091                                xml.push_str(&format!(">{}</w>", escape_xml_text(text)));
3092                            }
3093                            SsmlElement::AmazonEffect { name, text } => {
3094                                xml.push_str(&format!(
3095                                    "<amazon:effect name=\"{}\">{}</amazon:effect>",
3096                                    escape_xml_attr(name),
3097                                    escape_xml_text(text)
3098                                ));
3099                            }
3100                            SsmlElement::AmazonDomain { name, text } => {
3101                                xml.push_str(&format!(
3102                                    "<amazon:domain name=\"{}\">{}</amazon:domain>",
3103                                    escape_xml_attr(name),
3104                                    escape_xml_text(text)
3105                                ));
3106                            }
3107                        }
3108                    }
3109
3110                    xml.push_str("</Say>\n");
3111                }
3112                VoiceVerb::Play(play) => {
3113                    xml.push_str("  <Play");
3114                    if let Some(d) = &play.attributes.digits {
3115                        xml.push_str(&format!(" digits=\"{}\"", escape_xml_attr(d)));
3116                    }
3117                    if let Some(lc) = play.attributes.loop_count {
3118                        xml.push_str(&format!(" loop=\"{}\"", lc));
3119                    }
3120                    xml.push_str(">");
3121                    if let Some(url) = &play.url {
3122                        xml.push_str(&escape_xml_text(url));
3123                    }
3124                    xml.push_str("</Play>\n");
3125                }
3126                VoiceVerb::Pause(pause) => {
3127                    xml.push_str("  <Pause");
3128                    if let Some(len) = pause.attributes.length {
3129                        xml.push_str(&format!(" length=\"{}\"", len));
3130                    }
3131                    xml.push_str(" />\n");
3132                }
3133                VoiceVerb::Dial(dial) => {
3134                    xml.push_str("  <Dial");
3135                    if let Some(a) = &dial.attributes.action {
3136                        xml.push_str(&format!(" action=\"{}\"", escape_xml_attr(a)));
3137                    }
3138                    if let Some(m) = &dial.attributes.method {
3139                        xml.push_str(&format!(" method=\"{}\"", escape_xml_attr(m)));
3140                    }
3141                    if let Some(t) = dial.attributes.timeout {
3142                        xml.push_str(&format!(" timeout=\"{}\"", t));
3143                    }
3144                    if let Some(h) = dial.attributes.hangup_on_star {
3145                        xml.push_str(&format!(" hangupOnStar=\"{}\"", h));
3146                    }
3147                    if let Some(tl) = dial.attributes.time_limit {
3148                        xml.push_str(&format!(" timeLimit=\"{}\"", tl));
3149                    }
3150                    if let Some(c) = &dial.attributes.caller_id {
3151                        xml.push_str(&format!(" callerId=\"{}\"", escape_xml_attr(c)));
3152                    }
3153                    if let Some(cr) = &dial.attributes.call_reason {
3154                        xml.push_str(&format!(" callReason=\"{}\"", escape_xml_attr(cr)));
3155                    }
3156                    if let Some(r) = &dial.attributes.record {
3157                        xml.push_str(&format!(" record=\"{}\"", escape_xml_attr(r)));
3158                    }
3159                    if let Some(tr) = &dial.attributes.trim {
3160                        xml.push_str(&format!(" trim=\"{}\"", escape_xml_attr(tr)));
3161                    }
3162                    if let Some(rsc) = &dial.attributes.recording_status_callback {
3163                        xml.push_str(&format!(
3164                            " recordingStatusCallback=\"{}\"",
3165                            escape_xml_attr(rsc)
3166                        ));
3167                    }
3168                    if let Some(aob) = dial.attributes.answer_on_bridge {
3169                        xml.push_str(&format!(" answerOnBridge=\"{}\"", aob));
3170                    }
3171                    if let Some(rt) = &dial.attributes.ring_tone {
3172                        xml.push_str(&format!(" ringTone=\"{}\"", escape_xml_attr(rt)));
3173                    }
3174                    if let Some(e) = &dial.attributes.events {
3175                        xml.push_str(&format!(" events=\"{}\"", escape_xml_attr(e)));
3176                    }
3177                    if let Some(rm) = &dial.attributes.refer_method {
3178                        xml.push_str(&format!(" referMethod=\"{}\"", escape_xml_attr(rm)));
3179                    }
3180                    if let Some(ru) = &dial.attributes.refer_url {
3181                        xml.push_str(&format!(" referUrl=\"{}\"", escape_xml_attr(ru)));
3182                    }
3183                    if let Some(seq) = dial.attributes.sequential {
3184                        xml.push_str(&format!(" sequential=\"{}\"", seq));
3185                    }
3186                    if let Some(rt) = &dial.attributes.recording_track {
3187                        xml.push_str(&format!(" recordingTrack=\"{}\"", escape_xml_attr(rt)));
3188                    }
3189                    if let Some(rsce) = &dial.attributes.recording_status_callback_event {
3190                        let events = rsce.join(" ");
3191                        xml.push_str(&format!(
3192                            " recordingStatusCallbackEvent=\"{}\"",
3193                            escape_xml_attr(&events)
3194                        ));
3195                    }
3196                    if let Some(rscm) = &dial.attributes.recording_status_callback_method {
3197                        xml.push_str(&format!(
3198                            " recordingStatusCallbackMethod=\"{}\"",
3199                            escape_xml_attr(rscm)
3200                        ));
3201                    }
3202                    xml.push_str(">");
3203
3204                    // Add plain number if specified
3205                    if let Some(num) = &dial.number {
3206                        xml.push_str(num);
3207                    }
3208
3209                    // Add nested nouns
3210                    for noun in &dial.nested {
3211                        match noun {
3212                            DialNoun::Number(n) => {
3213                                xml.push_str("\n    <Number");
3214                                if let Some(sd) = &n.send_digits {
3215                                    xml.push_str(&format!(
3216                                        " sendDigits=\"{}\"",
3217                                        escape_xml_attr(sd)
3218                                    ));
3219                                }
3220                                if let Some(u) = &n.url {
3221                                    xml.push_str(&format!(" url=\"{}\"", escape_xml_attr(u)));
3222                                }
3223                                if let Some(m) = &n.method {
3224                                    xml.push_str(&format!(" method=\"{}\"", escape_xml_attr(m)));
3225                                }
3226                                if let Some(sc) = &n.status_callback {
3227                                    xml.push_str(&format!(
3228                                        " statusCallback=\"{}\"",
3229                                        escape_xml_attr(sc)
3230                                    ));
3231                                }
3232                                if let Some(sce) = &n.status_callback_event {
3233                                    let events = sce.join(" ");
3234                                    xml.push_str(&format!(
3235                                        " statusCallbackEvent=\"{}\"",
3236                                        escape_xml_attr(&events)
3237                                    ));
3238                                }
3239                                if let Some(scm) = &n.status_callback_method {
3240                                    xml.push_str(&format!(
3241                                        " statusCallbackMethod=\"{}\"",
3242                                        escape_xml_attr(scm)
3243                                    ));
3244                                }
3245                                if let Some(cr) = &n.call_reason {
3246                                    xml.push_str(&format!(
3247                                        " callReason=\"{}\"",
3248                                        escape_xml_attr(cr)
3249                                    ));
3250                                }
3251                                if let Some(byoc) = &n.byoc {
3252                                    xml.push_str(&format!(" byoc=\"{}\"", escape_xml_attr(byoc)));
3253                                }
3254                                if let Some(md) = &n.machine_detection {
3255                                    xml.push_str(&format!(
3256                                        " machineDetection=\"{}\"",
3257                                        escape_xml_attr(md)
3258                                    ));
3259                                }
3260                                if let Some(mdt) = n.machine_detection_timeout {
3261                                    xml.push_str(&format!(" machineDetectionTimeout=\"{}\"", mdt));
3262                                }
3263                                if let Some(mdst) = n.machine_detection_speech_threshold {
3264                                    xml.push_str(&format!(
3265                                        " machineDetectionSpeechThreshold=\"{}\"",
3266                                        mdst
3267                                    ));
3268                                }
3269                                if let Some(mdset) = n.machine_detection_speech_end_threshold {
3270                                    xml.push_str(&format!(
3271                                        " machineDetectionSpeechEndThreshold=\"{}\"",
3272                                        mdset
3273                                    ));
3274                                }
3275                                if let Some(mdsto) = n.machine_detection_silence_timeout {
3276                                    xml.push_str(&format!(
3277                                        " machineDetectionSilenceTimeout=\"{}\"",
3278                                        mdsto
3279                                    ));
3280                                }
3281                                if let Some(asc) = &n.amd_status_callback {
3282                                    xml.push_str(&format!(
3283                                        " amdStatusCallback=\"{}\"",
3284                                        escape_xml_attr(asc)
3285                                    ));
3286                                }
3287                                if let Some(ascm) = &n.amd_status_callback_method {
3288                                    xml.push_str(&format!(
3289                                        " amdStatusCallbackMethod=\"{}\"",
3290                                        escape_xml_attr(ascm)
3291                                    ));
3292                                }
3293                                xml.push_str(&format!(">{}</Number>", escape_xml_text(&n.number)));
3294                            }
3295                            DialNoun::Client(c) => {
3296                                xml.push_str("\n    <Client");
3297                                if let Some(u) = &c.url {
3298                                    xml.push_str(&format!(" url=\"{}\"", escape_xml_attr(u)));
3299                                }
3300                                if let Some(m) = &c.method {
3301                                    xml.push_str(&format!(" method=\"{}\"", escape_xml_attr(m)));
3302                                }
3303                                if let Some(sce) = &c.status_callback_event {
3304                                    xml.push_str(&format!(
3305                                        " statusCallbackEvent=\"{}\"",
3306                                        escape_xml_attr(&sce.join(" "))
3307                                    ));
3308                                }
3309                                if let Some(sc) = &c.status_callback {
3310                                    xml.push_str(&format!(
3311                                        " statusCallback=\"{}\"",
3312                                        escape_xml_attr(sc)
3313                                    ));
3314                                }
3315                                if let Some(scm) = &c.status_callback_method {
3316                                    xml.push_str(&format!(
3317                                        " statusCallbackMethod=\"{}\"",
3318                                        escape_xml_attr(scm)
3319                                    ));
3320                                }
3321                                if let Some(cnu) = &c.client_notification_url {
3322                                    xml.push_str(&format!(
3323                                        " clientNotificationUrl=\"{}\"",
3324                                        escape_xml_attr(cnu)
3325                                    ));
3326                                }
3327                                xml.push_str(&format!(
3328                                    ">{}</Client>",
3329                                    escape_xml_text(&c.identity)
3330                                ));
3331                            }
3332                            DialNoun::Conference(conf) => {
3333                                xml.push_str("\n    <Conference");
3334                                if let Some(m) = conf.muted {
3335                                    xml.push_str(&format!(" muted=\"{}\"", m));
3336                                }
3337                                if let Some(b) = &conf.beep {
3338                                    xml.push_str(&format!(" beep=\"{}\"", escape_xml_attr(b)));
3339                                }
3340                                if let Some(s) = conf.start_conference_on_enter {
3341                                    xml.push_str(&format!(" startConferenceOnEnter=\"{}\"", s));
3342                                }
3343                                if let Some(e) = conf.end_conference_on_exit {
3344                                    xml.push_str(&format!(" endConferenceOnExit=\"{}\"", e));
3345                                }
3346                                if let Some(w) = &conf.wait_url {
3347                                    xml.push_str(&format!(" waitUrl=\"{}\"", escape_xml_attr(w)));
3348                                }
3349                                if let Some(wm) = &conf.wait_method {
3350                                    xml.push_str(&format!(
3351                                        " waitMethod=\"{}\"",
3352                                        escape_xml_attr(wm)
3353                                    ));
3354                                }
3355                                if let Some(mp) = conf.max_participants {
3356                                    xml.push_str(&format!(" maxParticipants=\"{}\"", mp));
3357                                }
3358                                if let Some(r) = &conf.record {
3359                                    xml.push_str(&format!(" record=\"{}\"", escape_xml_attr(r)));
3360                                }
3361                                if let Some(reg) = &conf.region {
3362                                    xml.push_str(&format!(" region=\"{}\"", escape_xml_attr(reg)));
3363                                }
3364                                if let Some(c) = &conf.coach {
3365                                    xml.push_str(&format!(" coach=\"{}\"", escape_xml_attr(c)));
3366                                }
3367                                if let Some(t) = &conf.trim {
3368                                    xml.push_str(&format!(" trim=\"{}\"", escape_xml_attr(t)));
3369                                }
3370                                if let Some(sce) = &conf.status_callback_event {
3371                                    let events = sce.join(" ");
3372                                    xml.push_str(&format!(
3373                                        " statusCallbackEvent=\"{}\"",
3374                                        escape_xml_attr(&events)
3375                                    ));
3376                                }
3377                                if let Some(sc) = &conf.status_callback {
3378                                    xml.push_str(&format!(
3379                                        " statusCallback=\"{}\"",
3380                                        escape_xml_attr(sc)
3381                                    ));
3382                                }
3383                                if let Some(scm) = &conf.status_callback_method {
3384                                    xml.push_str(&format!(
3385                                        " statusCallbackMethod=\"{}\"",
3386                                        escape_xml_attr(scm)
3387                                    ));
3388                                }
3389                                if let Some(rsc) = &conf.recording_status_callback {
3390                                    xml.push_str(&format!(
3391                                        " recordingStatusCallback=\"{}\"",
3392                                        escape_xml_attr(rsc)
3393                                    ));
3394                                }
3395                                if let Some(rscm) = &conf.recording_status_callback_method {
3396                                    xml.push_str(&format!(
3397                                        " recordingStatusCallbackMethod=\"{}\"",
3398                                        escape_xml_attr(rscm)
3399                                    ));
3400                                }
3401                                if let Some(rsce) = &conf.recording_status_callback_event {
3402                                    let events = rsce.join(" ");
3403                                    xml.push_str(&format!(
3404                                        " recordingStatusCallbackEvent=\"{}\"",
3405                                        escape_xml_attr(&events)
3406                                    ));
3407                                }
3408                                if let Some(ecu) = &conf.event_callback_url {
3409                                    xml.push_str(&format!(
3410                                        " eventCallbackUrl=\"{}\"",
3411                                        escape_xml_attr(ecu)
3412                                    ));
3413                                }
3414                                if let Some(jbs) = &conf.jitter_buffer_size {
3415                                    xml.push_str(&format!(
3416                                        " jitterBufferSize=\"{}\"",
3417                                        escape_xml_attr(jbs)
3418                                    ));
3419                                }
3420                                if let Some(pl) = &conf.participant_label {
3421                                    xml.push_str(&format!(
3422                                        " participantLabel=\"{}\"",
3423                                        escape_xml_attr(pl)
3424                                    ));
3425                                }
3426                                if let Some(cstc) = &conf.call_sid_to_coach {
3427                                    xml.push_str(&format!(
3428                                        " callSidToCoach=\"{}\"",
3429                                        escape_xml_attr(cstc)
3430                                    ));
3431                                }
3432                                if let Some(boce) = conf.beep_on_customer_entrance {
3433                                    xml.push_str(&format!(" beepOnCustomerEntrance=\"{}\"", boce));
3434                                }
3435                                if let Some(coaching) = conf.coaching {
3436                                    xml.push_str(&format!(" coaching=\"{}\"", coaching));
3437                                }
3438                                xml.push_str(&format!(
3439                                    ">{}</Conference>",
3440                                    escape_xml_text(&conf.name)
3441                                ));
3442                            }
3443                            DialNoun::Queue(q) => {
3444                                xml.push_str("\n    <Queue");
3445                                if let Some(u) = &q.url {
3446                                    xml.push_str(&format!(" url=\"{}\"", escape_xml_attr(u)));
3447                                }
3448                                if let Some(m) = &q.method {
3449                                    xml.push_str(&format!(" method=\"{}\"", escape_xml_attr(m)));
3450                                }
3451                                xml.push_str(&format!(">{}</Queue>", escape_xml_text(&q.name)));
3452                            }
3453                            DialNoun::Sip(s) => {
3454                                xml.push_str("\n    <Sip");
3455                                if let Some(u) = &s.url {
3456                                    xml.push_str(&format!(" url=\"{}\"", escape_xml_attr(u)));
3457                                }
3458                                if let Some(m) = &s.method {
3459                                    xml.push_str(&format!(" method=\"{}\"", escape_xml_attr(m)));
3460                                }
3461                                if let Some(user) = &s.username {
3462                                    xml.push_str(&format!(
3463                                        " username=\"{}\"",
3464                                        escape_xml_attr(user)
3465                                    ));
3466                                }
3467                                if let Some(pass) = &s.password {
3468                                    xml.push_str(&format!(
3469                                        " password=\"{}\"",
3470                                        escape_xml_attr(pass)
3471                                    ));
3472                                }
3473                                if let Some(sce) = &s.status_callback_event {
3474                                    let events = sce.join(" ");
3475                                    xml.push_str(&format!(
3476                                        " statusCallbackEvent=\"{}\"",
3477                                        escape_xml_attr(&events)
3478                                    ));
3479                                }
3480                                if let Some(sc) = &s.status_callback {
3481                                    xml.push_str(&format!(
3482                                        " statusCallback=\"{}\"",
3483                                        escape_xml_attr(sc)
3484                                    ));
3485                                }
3486                                if let Some(scm) = &s.status_callback_method {
3487                                    xml.push_str(&format!(
3488                                        " statusCallbackMethod=\"{}\"",
3489                                        escape_xml_attr(scm)
3490                                    ));
3491                                }
3492                                if let Some(codecs) = &s.codecs {
3493                                    let codec_list = codecs.join(",");
3494                                    xml.push_str(&format!(
3495                                        " codecs=\"{}\"",
3496                                        escape_xml_attr(&codec_list)
3497                                    ));
3498                                }
3499                                xml.push_str(">");
3500
3501                                // Add custom SIP headers as child elements
3502                                if let Some(headers) = &s.custom_headers {
3503                                    for (name, value) in headers {
3504                                        xml.push_str(&format!(
3505                                            "\n      <SipHeader name=\"{}\" value=\"{}\"/>",
3506                                            escape_xml_attr(name),
3507                                            escape_xml_attr(value)
3508                                        ));
3509                                    }
3510                                    xml.push_str("\n      ");
3511                                }
3512
3513                                xml.push_str(&format!("{}</Sip>", escape_xml_text(&s.sip_url)));
3514                            }
3515                            DialNoun::Sim(sim) => {
3516                                xml.push_str(&format!(
3517                                    "\n    <Sim>{}</Sim>",
3518                                    escape_xml_text(&sim.sim_sid)
3519                                ));
3520                            }
3521                            DialNoun::Application(app) => {
3522                                let has_nested = !app.parameters.is_empty();
3523
3524                                xml.push_str("\n    <Application");
3525                                if let Some(sid) = &app.application_sid {
3526                                    xml.push_str(&format!(" sid=\"{}\"", escape_xml_attr(sid)));
3527                                }
3528                                if let Some(cid) = &app.customer_id {
3529                                    xml.push_str(&format!(
3530                                        " customerId=\"{}\"",
3531                                        escape_xml_attr(cid)
3532                                    ));
3533                                }
3534                                if let Some(cpt) = &app.copy_parent_to {
3535                                    xml.push_str(&format!(
3536                                        " copyParentTo=\"{}\"",
3537                                        escape_xml_attr(cpt)
3538                                    ));
3539                                }
3540
3541                                if has_nested {
3542                                    xml.push_str(">");
3543                                    for param in &app.parameters {
3544                                        xml.push_str("\n      <Parameter");
3545                                        if let Some(name) = &param.name {
3546                                            xml.push_str(&format!(
3547                                                " name=\"{}\"",
3548                                                escape_xml_attr(name)
3549                                            ));
3550                                        }
3551                                        if let Some(value) = &param.value {
3552                                            xml.push_str(&format!(
3553                                                " value=\"{}\"",
3554                                                escape_xml_attr(value)
3555                                            ));
3556                                        }
3557                                        xml.push_str(" />");
3558                                    }
3559                                    xml.push_str("\n    </Application>");
3560                                } else {
3561                                    xml.push_str(" />");
3562                                }
3563                            }
3564                            DialNoun::WhatsApp(wa) => {
3565                                xml.push_str("\n    <WhatsApp");
3566                                if let Some(u) = &wa.url {
3567                                    xml.push_str(&format!(" url=\"{}\"", escape_xml_attr(u)));
3568                                }
3569                                if let Some(m) = &wa.method {
3570                                    xml.push_str(&format!(" method=\"{}\"", escape_xml_attr(m)));
3571                                }
3572                                xml.push_str(&format!(
3573                                    ">{}</WhatsApp>",
3574                                    escape_xml_attr(&wa.phone_number)
3575                                ));
3576                            }
3577                        }
3578                    }
3579
3580                    if !dial.nested.is_empty() {
3581                        xml.push_str("\n  ");
3582                    }
3583                    xml.push_str("</Dial>\n");
3584                }
3585                VoiceVerb::Hangup(_) => {
3586                    xml.push_str("  <Hangup />\n");
3587                }
3588                VoiceVerb::Redirect(redirect) => {
3589                    xml.push_str("  <Redirect");
3590                    if let Some(m) = &redirect.attributes.method {
3591                        xml.push_str(&format!(" method=\"{}\"", escape_xml_attr(m)));
3592                    }
3593                    xml.push_str(&format!(">{}</Redirect>\n", escape_xml_text(&redirect.url)));
3594                }
3595                VoiceVerb::Reject(reject) => {
3596                    xml.push_str("  <Reject");
3597                    if let Some(r) = &reject.attributes.reason {
3598                        xml.push_str(&format!(" reason=\"{}\"", escape_xml_attr(r)));
3599                    }
3600                    xml.push_str(" />\n");
3601                }
3602                VoiceVerb::Gather(gather) => {
3603                    xml.push_str("  <Gather");
3604                    if let Some(i) = &gather.attributes.input {
3605                        xml.push_str(&format!(" input=\"{}\"", escape_xml_attr(&i.join(" "))));
3606                    }
3607                    if let Some(a) = &gather.attributes.action {
3608                        xml.push_str(&format!(" action=\"{}\"", escape_xml_attr(a)));
3609                    }
3610                    if let Some(m) = &gather.attributes.method {
3611                        xml.push_str(&format!(" method=\"{}\"", escape_xml_attr(m)));
3612                    }
3613                    if let Some(t) = gather.attributes.timeout {
3614                        xml.push_str(&format!(" timeout=\"{}\"", t));
3615                    }
3616                    if let Some(f) = &gather.attributes.finish_on_key {
3617                        xml.push_str(&format!(" finishOnKey=\"{}\"", escape_xml_attr(f)));
3618                    }
3619                    if let Some(n) = gather.attributes.num_digits {
3620                        xml.push_str(&format!(" numDigits=\"{}\"", n));
3621                    }
3622                    if let Some(p) = &gather.attributes.partial_result_callback {
3623                        xml.push_str(&format!(
3624                            " partialResultCallback=\"{}\"",
3625                            escape_xml_attr(p)
3626                        ));
3627                    }
3628                    if let Some(l) = &gather.attributes.language {
3629                        xml.push_str(&format!(" language=\"{}\"", escape_xml_attr(l)));
3630                    }
3631                    if let Some(h) = &gather.attributes.hints {
3632                        xml.push_str(&format!(" hints=\"{}\"", escape_xml_attr(h)));
3633                    }
3634                    if let Some(b) = gather.attributes.barge_in {
3635                        xml.push_str(&format!(" bargeIn=\"{}\"", b));
3636                    }
3637                    if let Some(st) = &gather.attributes.speech_timeout {
3638                        xml.push_str(&format!(" speechTimeout=\"{}\"", escape_xml_attr(st)));
3639                    }
3640                    if let Some(aer) = gather.attributes.action_on_empty_result {
3641                        xml.push_str(&format!(" actionOnEmptyResult=\"{}\"", aer));
3642                    }
3643                    if let Some(d) = gather.attributes.debug {
3644                        xml.push_str(&format!(" debug=\"{}\"", d));
3645                    }
3646                    if let Some(dd) = gather.attributes.dtmf_detection {
3647                        xml.push_str(&format!(" dtmfDetection=\"{}\"", dd));
3648                    }
3649                    if let Some(e) = gather.attributes.enhanced {
3650                        xml.push_str(&format!(" enhanced=\"{}\"", e));
3651                    }
3652                    if let Some(mst) = gather.attributes.max_speech_time {
3653                        xml.push_str(&format!(" maxSpeechTime=\"{}\"", mst));
3654                    }
3655                    if let Some(prcm) = &gather.attributes.partial_result_callback_method {
3656                        xml.push_str(&format!(
3657                            " partialResultCallbackMethod=\"{}\"",
3658                            escape_xml_attr(prcm)
3659                        ));
3660                    }
3661                    if let Some(pf) = gather.attributes.profanity_filter {
3662                        xml.push_str(&format!(" profanityFilter=\"{}\"", pf));
3663                    }
3664                    if let Some(sm) = &gather.attributes.speech_model {
3665                        xml.push_str(&format!(" speechModel=\"{}\"", escape_xml_attr(sm)));
3666                    }
3667
3668                    if gather.nested.is_empty() {
3669                        xml.push_str(" />\n");
3670                    } else {
3671                        xml.push_str(">\n");
3672                        for noun in &gather.nested {
3673                            match noun {
3674                                GatherNoun::Say(say) => {
3675                                    xml.push_str("    <Say");
3676                                    if let Some(v) = &say.attributes.voice {
3677                                        xml.push_str(&format!(" voice=\"{}\"", escape_xml_attr(v)));
3678                                    }
3679                                    if let Some(l) = &say.attributes.language {
3680                                        xml.push_str(&format!(
3681                                            " language=\"{}\"",
3682                                            escape_xml_attr(l)
3683                                        ));
3684                                    }
3685                                    if let Some(lc) = say.attributes.loop_count {
3686                                        xml.push_str(&format!(" loop=\"{}\"", lc));
3687                                    }
3688                                    xml.push_str(&format!(
3689                                        ">{}</Say>\n",
3690                                        escape_xml_text(&say.message)
3691                                    ));
3692                                }
3693                                GatherNoun::Play(play) => {
3694                                    xml.push_str("    <Play");
3695                                    if let Some(d) = &play.attributes.digits {
3696                                        xml.push_str(&format!(
3697                                            " digits=\"{}\"",
3698                                            escape_xml_attr(d)
3699                                        ));
3700                                    }
3701                                    if let Some(lc) = play.attributes.loop_count {
3702                                        xml.push_str(&format!(" loop=\"{}\"", lc));
3703                                    }
3704                                    xml.push_str(">");
3705                                    if let Some(url) = &play.url {
3706                                        xml.push_str(&escape_xml_text(url));
3707                                    }
3708                                    xml.push_str("</Play>\n");
3709                                }
3710                                GatherNoun::Pause(pause) => {
3711                                    xml.push_str("    <Pause");
3712                                    if let Some(len) = pause.attributes.length {
3713                                        xml.push_str(&format!(" length=\"{}\"", len));
3714                                    }
3715                                    xml.push_str(" />\n");
3716                                }
3717                            }
3718                        }
3719                        xml.push_str("  </Gather>\n");
3720                    }
3721                }
3722                VoiceVerb::Record(record) => {
3723                    xml.push_str("  <Record");
3724                    if let Some(a) = &record.attributes.action {
3725                        xml.push_str(&format!(" action=\"{}\"", escape_xml_attr(a)));
3726                    }
3727                    if let Some(m) = &record.attributes.method {
3728                        xml.push_str(&format!(" method=\"{}\"", escape_xml_attr(m)));
3729                    }
3730                    if let Some(t) = record.attributes.timeout {
3731                        xml.push_str(&format!(" timeout=\"{}\"", t));
3732                    }
3733                    if let Some(f) = &record.attributes.finish_on_key {
3734                        xml.push_str(&format!(" finishOnKey=\"{}\"", escape_xml_attr(f)));
3735                    }
3736                    if let Some(ml) = record.attributes.max_length {
3737                        xml.push_str(&format!(" maxLength=\"{}\"", ml));
3738                    }
3739                    if let Some(pb) = record.attributes.play_beep {
3740                        xml.push_str(&format!(" playBeep=\"{}\"", pb));
3741                    }
3742                    if let Some(tr) = &record.attributes.trim {
3743                        xml.push_str(&format!(" trim=\"{}\"", escape_xml_attr(tr)));
3744                    }
3745                    if let Some(rsc) = &record.attributes.recording_status_callback {
3746                        xml.push_str(&format!(
3747                            " recordingStatusCallback=\"{}\"",
3748                            escape_xml_attr(rsc)
3749                        ));
3750                    }
3751                    if let Some(rsce) = &record.attributes.recording_status_callback_event {
3752                        let events = rsce.join(" ");
3753                        xml.push_str(&format!(
3754                            " recordingStatusCallbackEvent=\"{}\"",
3755                            escape_xml_attr(&events)
3756                        ));
3757                    }
3758                    if let Some(rscm) = &record.attributes.recording_status_callback_method {
3759                        xml.push_str(&format!(
3760                            " recordingStatusCallbackMethod=\"{}\"",
3761                            escape_xml_attr(rscm)
3762                        ));
3763                    }
3764                    if let Some(t) = record.attributes.transcribe {
3765                        xml.push_str(&format!(" transcribe=\"{}\"", t));
3766                    }
3767                    if let Some(tc) = &record.attributes.transcribe_callback {
3768                        xml.push_str(&format!(" transcribeCallback=\"{}\"", escape_xml_attr(tc)));
3769                    }
3770                    if let Some(rc) = &record.attributes.recording_channels {
3771                        xml.push_str(&format!(" recordingChannels=\"{}\"", escape_xml_attr(rc)));
3772                    }
3773                    if let Some(rt) = &record.attributes.recording_track {
3774                        xml.push_str(&format!(" recordingTrack=\"{}\"", escape_xml_attr(rt)));
3775                    }
3776                    xml.push_str(" />\n");
3777                }
3778                VoiceVerb::Connect(connect) => {
3779                    xml.push_str("  <Connect");
3780                    if let Some(a) = &connect.attributes.action {
3781                        xml.push_str(&format!(" action=\"{}\"", escape_xml_attr(a)));
3782                    }
3783                    if let Some(m) = &connect.attributes.method {
3784                        xml.push_str(&format!(" method=\"{}\"", escape_xml_attr(m)));
3785                    }
3786
3787                    if connect.nested.is_empty() {
3788                        xml.push_str(" />\n");
3789                    } else {
3790                        xml.push_str(">");
3791
3792                        for noun in &connect.nested {
3793                            match noun {
3794                                ConnectNoun::Stream(stream) => {
3795                                    xml.push_str("\n    <Stream");
3796                                    if let Some(n) = &stream.name {
3797                                        xml.push_str(&format!(" name=\"{}\"", escape_xml_attr(n)));
3798                                    }
3799                                    if let Some(cn) = &stream.connector_name {
3800                                        xml.push_str(&format!(
3801                                            " connectorName=\"{}\"",
3802                                            escape_xml_attr(cn)
3803                                        ));
3804                                    }
3805                                    if let Some(u) = &stream.url {
3806                                        xml.push_str(&format!(" url=\"{}\"", escape_xml_attr(u)));
3807                                    }
3808                                    if let Some(t) = &stream.track {
3809                                        xml.push_str(&format!(" track=\"{}\"", escape_xml_attr(t)));
3810                                    }
3811                                    if stream.parameters.is_empty() {
3812                                        xml.push_str(" />");
3813                                    } else {
3814                                        xml.push_str(">");
3815                                        for param in &stream.parameters {
3816                                            xml.push_str("\n      <Parameter");
3817                                            if let Some(n) = &param.name {
3818                                                xml.push_str(&format!(
3819                                                    " name=\"{}\"",
3820                                                    escape_xml_attr(n)
3821                                                ));
3822                                            }
3823                                            if let Some(v) = &param.value {
3824                                                xml.push_str(&format!(
3825                                                    " value=\"{}\"",
3826                                                    escape_xml_attr(v)
3827                                                ));
3828                                            }
3829                                            xml.push_str(" />");
3830                                        }
3831                                        xml.push_str("\n    </Stream>");
3832                                    }
3833                                }
3834                                ConnectNoun::Room(room) => {
3835                                    xml.push_str("\n    <Room");
3836                                    if let Some(pi) = &room.participant_identity {
3837                                        xml.push_str(&format!(
3838                                            " participantIdentity=\"{}\"",
3839                                            escape_xml_attr(pi)
3840                                        ));
3841                                    }
3842                                    xml.push_str(">");
3843                                    if let Some(n) = &room.name {
3844                                        xml.push_str(&escape_xml_attr(n));
3845                                    }
3846                                    xml.push_str("</Room>");
3847                                }
3848                                ConnectNoun::Conversation(conv) => {
3849                                    xml.push_str("\n    <Conversation");
3850                                    if let Some(sid) = &conv.service_instance_sid {
3851                                        xml.push_str(&format!(
3852                                            " serviceInstanceSid=\"{}\"",
3853                                            escape_xml_attr(sid)
3854                                        ));
3855                                    }
3856                                    xml.push_str(" />");
3857                                }
3858                                ConnectNoun::VirtualAgent(va) => {
3859                                    xml.push_str("\n    <VirtualAgent");
3860                                    if let Some(cn) = &va.connector_name {
3861                                        xml.push_str(&format!(
3862                                            " connectorName=\"{}\"",
3863                                            escape_xml_attr(cn)
3864                                        ));
3865                                    }
3866                                    if let Some(l) = &va.language {
3867                                        xml.push_str(&format!(
3868                                            " language=\"{}\"",
3869                                            escape_xml_attr(l)
3870                                        ));
3871                                    }
3872                                    if va.parameters.is_empty() {
3873                                        xml.push_str(" />");
3874                                    } else {
3875                                        xml.push_str(">");
3876                                        for param in &va.parameters {
3877                                            xml.push_str("\n      <Parameter");
3878                                            if let Some(n) = &param.name {
3879                                                xml.push_str(&format!(
3880                                                    " name=\"{}\"",
3881                                                    escape_xml_attr(n)
3882                                                ));
3883                                            }
3884                                            if let Some(v) = &param.value {
3885                                                xml.push_str(&format!(
3886                                                    " value=\"{}\"",
3887                                                    escape_xml_attr(v)
3888                                                ));
3889                                            }
3890                                            xml.push_str(" />");
3891                                        }
3892                                        xml.push_str("\n    </VirtualAgent>");
3893                                    }
3894                                }
3895                                ConnectNoun::Autopilot(ap) => {
3896                                    xml.push_str("\n    <Autopilot");
3897                                    xml.push_str(">");
3898                                    if let Some(n) = &ap.name {
3899                                        xml.push_str(&escape_xml_attr(n));
3900                                    }
3901                                    xml.push_str("</Autopilot>");
3902                                }
3903                                ConnectNoun::AiSession(ai) => {
3904                                    xml.push_str("\n    <AiSession");
3905                                    if let Some(sid) = &ai.assistant_sid {
3906                                        xml.push_str(&format!(
3907                                            " assistantSid=\"{}\"",
3908                                            escape_xml_attr(sid)
3909                                        ));
3910                                    }
3911                                    xml.push_str(" />");
3912                                }
3913                                ConnectNoun::ConversationRelaySession(crs) => {
3914                                    xml.push_str("\n    <ConversationRelaySession");
3915                                    if let Some(c) = &crs.connector {
3916                                        xml.push_str(&format!(
3917                                            " connector=\"{}\"",
3918                                            escape_xml_attr(c)
3919                                        ));
3920                                    }
3921                                    if let Some(sc) = &crs.session_configuration {
3922                                        xml.push_str(&format!(
3923                                            " sessionConfiguration=\"{}\"",
3924                                            escape_xml_attr(sc)
3925                                        ));
3926                                    }
3927                                    xml.push_str(" />");
3928                                }
3929                                ConnectNoun::Assistant(asst) => {
3930                                    xml.push_str("\n    <Assistant");
3931                                    if let Some(sid) = &asst.sid {
3932                                        xml.push_str(&format!(" sid=\"{}\"", escape_xml_attr(sid)));
3933                                    }
3934                                    xml.push_str(" />");
3935                                }
3936                                ConnectNoun::ConversationRelay(cr) => {
3937                                    let has_nested =
3938                                        !cr.languages.is_empty() || !cr.parameters.is_empty();
3939
3940                                    xml.push_str("\n    <ConversationRelay");
3941                                    if let Some(u) = &cr.url {
3942                                        xml.push_str(&format!(" url=\"{}\"", escape_xml_attr(u)));
3943                                    }
3944                                    if let Some(g) = &cr.welcome_greeting {
3945                                        xml.push_str(&format!(
3946                                            " welcomeGreeting=\"{}\"",
3947                                            escape_xml_attr(g)
3948                                        ));
3949                                    }
3950                                    if let Some(v) = &cr.voice {
3951                                        xml.push_str(&format!(" voice=\"{}\"", escape_xml_attr(v)));
3952                                    }
3953                                    if let Some(l) = &cr.language {
3954                                        xml.push_str(&format!(
3955                                            " language=\"{}\"",
3956                                            escape_xml_attr(l)
3957                                        ));
3958                                    }
3959                                    if let Some(d) = cr.dtmf_detection {
3960                                        xml.push_str(&format!(" dtmfDetection=\"{}\"", d));
3961                                    }
3962                                    if let Some(i) = cr.interruptible {
3963                                        xml.push_str(&format!(" interruptible=\"{}\"", i));
3964                                    }
3965                                    if let Some(s) = &cr.interruption_sensitivity {
3966                                        xml.push_str(&format!(
3967                                            " interruptionSensitivity=\"{}\"",
3968                                            escape_xml_attr(s)
3969                                        ));
3970                                    }
3971                                    if let Some(m) = &cr.speech_model {
3972                                        xml.push_str(&format!(
3973                                            " speechModel=\"{}\"",
3974                                            escape_xml_attr(m)
3975                                        ));
3976                                    }
3977                                    if let Some(p) = cr.profanity_filter {
3978                                        xml.push_str(&format!(" profanityFilter=\"{}\"", p));
3979                                    }
3980                                    if let Some(t) = cr.transcription_enabled {
3981                                        xml.push_str(&format!(" transcriptionEnabled=\"{}\"", t));
3982                                    }
3983                                    if let Some(sc) = &cr.status_callback {
3984                                        xml.push_str(&format!(
3985                                            " statusCallback=\"{}\"",
3986                                            escape_xml_attr(sc)
3987                                        ));
3988                                    }
3989                                    if let Some(scm) = &cr.status_callback_method {
3990                                        xml.push_str(&format!(
3991                                            " statusCallbackMethod=\"{}\"",
3992                                            escape_xml_attr(scm)
3993                                        ));
3994                                    }
3995                                    if let Some(md) = cr.max_duration {
3996                                        xml.push_str(&format!(" maxDuration=\"{}\"", md));
3997                                    }
3998
3999                                    if has_nested {
4000                                        xml.push_str(">");
4001
4002                                        // Add Language elements
4003                                        for lang in &cr.languages {
4004                                            xml.push_str("\n      <Language");
4005                                            if let Some(code) = &lang.language_code {
4006                                                xml.push_str(&format!(
4007                                                    " code=\"{}\"",
4008                                                    escape_xml_attr(code)
4009                                                ));
4010                                            }
4011                                            if let Some(tts) = &lang.tts_provider {
4012                                                xml.push_str(&format!(
4013                                                    " ttsProvider=\"{}\"",
4014                                                    escape_xml_attr(tts)
4015                                                ));
4016                                            }
4017                                            if let Some(stt) = &lang.stt_provider {
4018                                                xml.push_str(&format!(
4019                                                    " sttProvider=\"{}\"",
4020                                                    escape_xml_attr(stt)
4021                                                ));
4022                                            }
4023                                            xml.push_str(" />");
4024                                        }
4025
4026                                        // Add Parameter elements
4027                                        for param in &cr.parameters {
4028                                            xml.push_str("\n      <Parameter");
4029                                            if let Some(name) = &param.name {
4030                                                xml.push_str(&format!(
4031                                                    " name=\"{}\"",
4032                                                    escape_xml_attr(name)
4033                                                ));
4034                                            }
4035                                            if let Some(value) = &param.value {
4036                                                xml.push_str(&format!(
4037                                                    " value=\"{}\"",
4038                                                    escape_xml_attr(value)
4039                                                ));
4040                                            }
4041                                            xml.push_str(" />");
4042                                        }
4043
4044                                        xml.push_str("\n    </ConversationRelay>");
4045                                    } else {
4046                                        xml.push_str(" />");
4047                                    }
4048                                }
4049                            }
4050                        }
4051
4052                        xml.push_str("\n  </Connect>\n");
4053                    }
4054                }
4055                VoiceVerb::Stop(_) => {
4056                    xml.push_str("  <Stop />\n");
4057                }
4058                VoiceVerb::Echo(_) => {
4059                    xml.push_str("  <Echo />\n");
4060                }
4061                VoiceVerb::Leave(_) => {
4062                    xml.push_str("  <Leave />\n");
4063                }
4064                VoiceVerb::Sms(sms) => {
4065                    xml.push_str("  <Sms");
4066                    if let Some(t) = &sms.attributes.to {
4067                        xml.push_str(&format!(" to=\"{}\"", escape_xml_attr(t)));
4068                    }
4069                    if let Some(f) = &sms.attributes.from {
4070                        xml.push_str(&format!(" from=\"{}\"", escape_xml_attr(f)));
4071                    }
4072                    if let Some(a) = &sms.attributes.action {
4073                        xml.push_str(&format!(" action=\"{}\"", escape_xml_attr(a)));
4074                    }
4075                    if let Some(m) = &sms.attributes.method {
4076                        xml.push_str(&format!(" method=\"{}\"", escape_xml_attr(m)));
4077                    }
4078                    if let Some(sc) = &sms.attributes.status_callback {
4079                        xml.push_str(&format!(" statusCallback=\"{}\"", escape_xml_attr(sc)));
4080                    }
4081                    xml.push_str(&format!(">{}</Sms>\n", escape_xml_text(&sms.message)));
4082                }
4083                VoiceVerb::Enqueue(enqueue) => {
4084                    xml.push_str("  <Enqueue");
4085                    if let Some(a) = &enqueue.attributes.action {
4086                        xml.push_str(&format!(" action=\"{}\"", escape_xml_attr(a)));
4087                    }
4088                    if let Some(m) = &enqueue.attributes.method {
4089                        xml.push_str(&format!(" method=\"{}\"", escape_xml_attr(m)));
4090                    }
4091                    if let Some(wu) = &enqueue.attributes.wait_url {
4092                        xml.push_str(&format!(" waitUrl=\"{}\"", escape_xml_attr(wu)));
4093                    }
4094                    if let Some(wum) = &enqueue.attributes.wait_url_method {
4095                        xml.push_str(&format!(" waitUrlMethod=\"{}\"", escape_xml_attr(wum)));
4096                    }
4097                    if let Some(ws) = &enqueue.attributes.workflow_sid {
4098                        xml.push_str(&format!(" workflowSid=\"{}\"", escape_xml_attr(ws)));
4099                    }
4100                    if let Some(mqs) = enqueue.attributes.max_queue_size {
4101                        xml.push_str(&format!(" maxQueueSize=\"{}\"", mqs));
4102                    }
4103
4104                    if enqueue.name.is_none() && enqueue.task.is_none() {
4105                        xml.push_str(" />\n");
4106                    } else {
4107                        xml.push_str(">");
4108                        if let Some(name) = &enqueue.name {
4109                            xml.push_str(&escape_xml_attr(name));
4110                        }
4111                        if let Some(task) = &enqueue.task {
4112                            xml.push_str("\n    <Task");
4113                            if let Some(sid) = &task.task_sid {
4114                                xml.push_str(&format!(" sid=\"{}\"", escape_xml_attr(sid)));
4115                            }
4116                            xml.push_str(" />");
4117                        }
4118                        xml.push_str("\n  </Enqueue>\n");
4119                    }
4120                }
4121                VoiceVerb::Queue(queue) => {
4122                    xml.push_str("  <Queue");
4123                    if let Some(u) = &queue.attributes.url {
4124                        xml.push_str(&format!(" url=\"{}\"", escape_xml_attr(u)));
4125                    }
4126                    if let Some(m) = &queue.attributes.method {
4127                        xml.push_str(&format!(" method=\"{}\"", escape_xml_attr(m)));
4128                    }
4129                    if let Some(rs) = &queue.attributes.reservation_sid {
4130                        xml.push_str(&format!(" reservationSid=\"{}\"", escape_xml_attr(rs)));
4131                    }
4132                    if let Some(pwas) = &queue.attributes.post_work_activity_sid {
4133                        xml.push_str(&format!(
4134                            " postWorkActivitySid=\"{}\"",
4135                            escape_xml_attr(pwas)
4136                        ));
4137                    }
4138                    xml.push_str(&format!(">{}</Queue>\n", escape_xml_attr(&queue.name)));
4139                }
4140                VoiceVerb::Pay(pay) => {
4141                    xml.push_str("  <Pay");
4142                    if let Some(i) = &pay.attributes.input {
4143                        xml.push_str(&format!(" input=\"{}\"", escape_xml_attr(i)));
4144                    }
4145                    if let Some(a) = &pay.attributes.action {
4146                        xml.push_str(&format!(" action=\"{}\"", escape_xml_attr(a)));
4147                    }
4148                    if let Some(ca) = &pay.attributes.charge_amount {
4149                        xml.push_str(&format!(" chargeAmount=\"{}\"", escape_xml_attr(ca)));
4150                    }
4151                    if let Some(c) = &pay.attributes.currency {
4152                        xml.push_str(&format!(" currency=\"{}\"", escape_xml_attr(c)));
4153                    }
4154                    if let Some(pc) = &pay.attributes.payment_connector {
4155                        xml.push_str(&format!(" paymentConnector=\"{}\"", escape_xml_attr(pc)));
4156                    }
4157                    if let Some(pm) = &pay.attributes.payment_method {
4158                        xml.push_str(&format!(" paymentMethod=\"{}\"", escape_xml_attr(pm)));
4159                    }
4160                    if let Some(t) = pay.attributes.timeout {
4161                        xml.push_str(&format!(" timeout=\"{}\"", t));
4162                    }
4163                    if let Some(scm) = &pay.attributes.status_callback_method {
4164                        xml.push_str(&format!(
4165                            " statusCallbackMethod=\"{}\"",
4166                            escape_xml_attr(scm)
4167                        ));
4168                    }
4169                    if let Some(sc) = &pay.attributes.status_callback {
4170                        xml.push_str(&format!(" statusCallback=\"{}\"", escape_xml_attr(sc)));
4171                    }
4172
4173                    if pay.prompts.is_empty() && pay.parameters.is_empty() {
4174                        xml.push_str(" />\n");
4175                    } else {
4176                        xml.push_str(">");
4177
4178                        for prompt in &pay.prompts {
4179                            xml.push_str("\n    <Prompt");
4180                            if let Some(f) = &prompt.attributes.for_attr {
4181                                xml.push_str(&format!(" for=\"{}\"", escape_xml_attr(f)));
4182                            }
4183                            if let Some(a) = &prompt.attributes.attempt {
4184                                let attempts = a
4185                                    .iter()
4186                                    .map(|n| n.to_string())
4187                                    .collect::<Vec<_>>()
4188                                    .join(" ");
4189                                xml.push_str(&format!(
4190                                    " attempt=\"{}\"",
4191                                    escape_xml_attr(&attempts)
4192                                ));
4193                            }
4194                            if let Some(ct) = &prompt.attributes.card_type {
4195                                let types = ct.join(" ");
4196                                xml.push_str(&format!(" cardType=\"{}\"", escape_xml_attr(&types)));
4197                            }
4198                            if let Some(et) = &prompt.attributes.error_type {
4199                                let types = et.join(" ");
4200                                xml.push_str(&format!(
4201                                    " errorType=\"{}\"",
4202                                    escape_xml_attr(&types)
4203                                ));
4204                            }
4205                            xml.push_str(" />");
4206                        }
4207
4208                        for param in &pay.parameters {
4209                            xml.push_str("\n    <Parameter");
4210                            if let Some(n) = &param.name {
4211                                xml.push_str(&format!(" name=\"{}\"", escape_xml_attr(n)));
4212                            }
4213                            if let Some(v) = &param.value {
4214                                xml.push_str(&format!(" value=\"{}\"", escape_xml_attr(v)));
4215                            }
4216                            xml.push_str(" />");
4217                        }
4218
4219                        xml.push_str("\n  </Pay>\n");
4220                    }
4221                }
4222                VoiceVerb::Prompt(prompt) => {
4223                    xml.push_str("  <Prompt");
4224                    if let Some(f) = &prompt.attributes.for_attr {
4225                        xml.push_str(&format!(" for=\"{}\"", escape_xml_attr(f)));
4226                    }
4227                    if let Some(a) = &prompt.attributes.attempt {
4228                        let attempts = a
4229                            .iter()
4230                            .map(|n| n.to_string())
4231                            .collect::<Vec<_>>()
4232                            .join(" ");
4233                        xml.push_str(&format!(" attempt=\"{}\"", escape_xml_attr(&attempts)));
4234                    }
4235                    if let Some(ct) = &prompt.attributes.card_type {
4236                        let types = ct.join(" ");
4237                        xml.push_str(&format!(" cardType=\"{}\"", escape_xml_attr(&types)));
4238                    }
4239                    if let Some(et) = &prompt.attributes.error_type {
4240                        let types = et.join(" ");
4241                        xml.push_str(&format!(" errorType=\"{}\"", escape_xml_attr(&types)));
4242                    }
4243                    xml.push_str(" />\n");
4244                }
4245                VoiceVerb::Refer(refer) => {
4246                    xml.push_str("  <Refer");
4247                    if let Some(a) = &refer.attributes.action {
4248                        xml.push_str(&format!(" action=\"{}\"", escape_xml_attr(a)));
4249                    }
4250                    if let Some(m) = &refer.attributes.method {
4251                        xml.push_str(&format!(" method=\"{}\"", escape_xml_attr(m)));
4252                    }
4253
4254                    if let Some(sip) = &refer.refer_sip {
4255                        xml.push_str(">");
4256                        xml.push_str(&format!(
4257                            "\n    <Sip>{}</Sip>",
4258                            escape_xml_attr(&sip.sip_url)
4259                        ));
4260                        xml.push_str("\n  </Refer>\n");
4261                    } else {
4262                        xml.push_str(" />\n");
4263                    }
4264                }
4265                VoiceVerb::Start(start) => {
4266                    xml.push_str("  <Start");
4267                    if let Some(a) = &start.attributes.action {
4268                        xml.push_str(&format!(" action=\"{}\"", escape_xml_attr(a)));
4269                    }
4270                    if let Some(m) = &start.attributes.method {
4271                        xml.push_str(&format!(" method=\"{}\"", escape_xml_attr(m)));
4272                    }
4273
4274                    if start.nested.is_empty() {
4275                        xml.push_str(" />\n");
4276                    } else {
4277                        xml.push_str(">");
4278
4279                        for noun in &start.nested {
4280                            match noun {
4281                                StartNoun::Stream(stream) => {
4282                                    xml.push_str("\n    <Stream");
4283                                    if let Some(n) = &stream.name {
4284                                        xml.push_str(&format!(" name=\"{}\"", escape_xml_attr(n)));
4285                                    }
4286                                    if let Some(cn) = &stream.connector_name {
4287                                        xml.push_str(&format!(
4288                                            " connectorName=\"{}\"",
4289                                            escape_xml_attr(cn)
4290                                        ));
4291                                    }
4292                                    if let Some(u) = &stream.url {
4293                                        xml.push_str(&format!(" url=\"{}\"", escape_xml_attr(u)));
4294                                    }
4295                                    if let Some(t) = &stream.track {
4296                                        xml.push_str(&format!(" track=\"{}\"", escape_xml_attr(t)));
4297                                    }
4298                                    if let Some(sc) = &stream.status_callback {
4299                                        xml.push_str(&format!(
4300                                            " statusCallback=\"{}\"",
4301                                            escape_xml_attr(sc)
4302                                        ));
4303                                    }
4304                                    if let Some(scm) = &stream.status_callback_method {
4305                                        xml.push_str(&format!(
4306                                            " statusCallbackMethod=\"{}\"",
4307                                            escape_xml_attr(scm)
4308                                        ));
4309                                    }
4310
4311                                    if stream.parameters.is_empty() {
4312                                        xml.push_str(" />");
4313                                    } else {
4314                                        xml.push_str(">");
4315                                        for param in &stream.parameters {
4316                                            xml.push_str("\n      <Parameter");
4317                                            if let Some(n) = &param.name {
4318                                                xml.push_str(&format!(
4319                                                    " name=\"{}\"",
4320                                                    escape_xml_attr(n)
4321                                                ));
4322                                            }
4323                                            if let Some(v) = &param.value {
4324                                                xml.push_str(&format!(
4325                                                    " value=\"{}\"",
4326                                                    escape_xml_attr(v)
4327                                                ));
4328                                            }
4329                                            xml.push_str(" />");
4330                                        }
4331                                        xml.push_str("\n    </Stream>");
4332                                    }
4333                                }
4334                                StartNoun::Siprec(siprec) => {
4335                                    xml.push_str("\n    <Siprec");
4336                                    if let Some(n) = &siprec.name {
4337                                        xml.push_str(&format!(" name=\"{}\"", escape_xml_attr(n)));
4338                                    }
4339                                    if let Some(cn) = &siprec.connector_name {
4340                                        xml.push_str(&format!(
4341                                            " connectorName=\"{}\"",
4342                                            escape_xml_attr(cn)
4343                                        ));
4344                                    }
4345                                    if let Some(t) = &siprec.track {
4346                                        xml.push_str(&format!(" track=\"{}\"", escape_xml_attr(t)));
4347                                    }
4348                                    xml.push_str(" />");
4349                                }
4350                                StartNoun::Transcription(trans) => {
4351                                    xml.push_str("\n    <Transcription");
4352                                    if let Some(n) = &trans.attributes.name {
4353                                        xml.push_str(&format!(" name=\"{}\"", escape_xml_attr(n)));
4354                                    }
4355                                    if let Some(t) = &trans.attributes.track {
4356                                        xml.push_str(&format!(" track=\"{}\"", escape_xml_attr(t)));
4357                                    }
4358                                    if let Some(lc) = &trans.attributes.language_code {
4359                                        xml.push_str(&format!(
4360                                            " languageCode=\"{}\"",
4361                                            escape_xml_attr(lc)
4362                                        ));
4363                                    }
4364                                    if let Some(enable) =
4365                                        trans.attributes.enable_automatic_punctuation
4366                                    {
4367                                        xml.push_str(&format!(
4368                                            " enableAutomaticPunctuation=\"{}\"",
4369                                            enable
4370                                        ));
4371                                    }
4372                                    if let Some(h) = &trans.attributes.hints {
4373                                        xml.push_str(&format!(" hints=\"{}\"", escape_xml_attr(h)));
4374                                    }
4375                                    if let Some(label) = &trans.attributes.inbound_track_label {
4376                                        xml.push_str(&format!(
4377                                            " inboundTrackLabel=\"{}\"",
4378                                            escape_xml_attr(label)
4379                                        ));
4380                                    }
4381                                    if let Some(service) = &trans.attributes.intelligence_service {
4382                                        xml.push_str(&format!(
4383                                            " intelligenceService=\"{}\"",
4384                                            escape_xml_attr(service)
4385                                        ));
4386                                    }
4387                                    if let Some(label) = &trans.attributes.outbound_track_label {
4388                                        xml.push_str(&format!(
4389                                            " outboundTrackLabel=\"{}\"",
4390                                            escape_xml_attr(label)
4391                                        ));
4392                                    }
4393                                    if let Some(enable) = trans.attributes.partial_results {
4394                                        xml.push_str(&format!(" partialResults=\"{}\"", enable));
4395                                    }
4396                                    if let Some(enable) = trans.attributes.profanity_filter {
4397                                        xml.push_str(&format!(" profanityFilter=\"{}\"", enable));
4398                                    }
4399                                    if let Some(model) = &trans.attributes.speech_model {
4400                                        xml.push_str(&format!(
4401                                            " speechModel=\"{}\"",
4402                                            escape_xml_attr(model)
4403                                        ));
4404                                    }
4405                                    if let Some(method) = &trans.attributes.status_callback_method {
4406                                        xml.push_str(&format!(
4407                                            " statusCallbackMethod=\"{}\"",
4408                                            escape_xml_attr(method)
4409                                        ));
4410                                    }
4411                                    if let Some(url) = &trans.attributes.status_callback_url {
4412                                        xml.push_str(&format!(
4413                                            " statusCallbackUrl=\"{}\"",
4414                                            escape_xml_attr(url)
4415                                        ));
4416                                    }
4417                                    if let Some(engine) = &trans.attributes.transcription_engine {
4418                                        xml.push_str(&format!(
4419                                            " transcriptionEngine=\"{}\"",
4420                                            escape_xml_attr(engine)
4421                                        ));
4422                                    }
4423                                    xml.push_str(" />");
4424                                }
4425                                StartNoun::Recording(rec) => {
4426                                    xml.push_str("\n    <Recording");
4427                                    if let Some(cb) = &rec.recording_status_callback {
4428                                        xml.push_str(&format!(
4429                                            " recordingStatusCallback=\"{}\"",
4430                                            escape_xml_attr(cb)
4431                                        ));
4432                                    }
4433                                    if let Some(m) = &rec.recording_status_callback_method {
4434                                        xml.push_str(&format!(
4435                                            " recordingStatusCallbackMethod=\"{}\"",
4436                                            escape_xml_attr(m)
4437                                        ));
4438                                    }
4439                                    if let Some(e) = &rec.recording_status_callback_event {
4440                                        xml.push_str(&format!(
4441                                            " recordingStatusCallbackEvent=\"{}\"",
4442                                            escape_xml_attr(e)
4443                                        ));
4444                                    }
4445                                    if let Some(tr) = &rec.trim {
4446                                        xml.push_str(&format!(" trim=\"{}\"", escape_xml_attr(tr)));
4447                                    }
4448                                    if let Some(t) = &rec.track {
4449                                        xml.push_str(&format!(" track=\"{}\"", escape_xml_attr(t)));
4450                                    }
4451                                    if let Some(ch) = &rec.channels {
4452                                        xml.push_str(&format!(
4453                                            " channels=\"{}\"",
4454                                            escape_xml_attr(ch)
4455                                        ));
4456                                    }
4457                                    xml.push_str(" />");
4458                                }
4459                            }
4460                        }
4461
4462                        xml.push_str("\n  </Start>\n");
4463                    }
4464                }
4465            }
4466        }
4467
4468        xml.push_str("</Response>");
4469
4470        // Add comments after Response
4471        for comment in &self.comments_after {
4472            xml.push_str(&format!("\n<!-- {} -->", escape_xml_text(comment)));
4473        }
4474
4475        xml
4476    }
4477}