use crate::xml_escape::{escape_xml_attr, escape_xml_text};
use crate::TwiML;
#[derive(Debug, Clone, Default)]
pub struct MessageAttributes {
pub action: Option<String>,
pub from: Option<String>,
pub method: Option<String>,
pub status_callback: Option<String>,
pub to: Option<String>,
}
impl MessageAttributes {
pub fn new() -> Self {
Self::default()
}
pub fn action(mut self, action: impl Into<String>) -> Self {
self.action = Some(action.into());
self
}
pub fn from(mut self, from: impl Into<String>) -> Self {
self.from = Some(from.into());
self
}
pub fn method(mut self, method: impl Into<String>) -> Self {
self.method = Some(method.into());
self
}
pub fn status_callback(mut self, status_callback: impl Into<String>) -> Self {
self.status_callback = Some(status_callback.into());
self
}
pub fn to(mut self, to: impl Into<String>) -> Self {
self.to = Some(to.into());
self
}
}
#[derive(Debug, Clone, Default)]
pub struct RedirectAttributes {
pub method: Option<String>,
}
impl RedirectAttributes {
pub fn new() -> Self {
Self::default()
}
pub fn method(mut self, method: impl Into<String>) -> Self {
self.method = Some(method.into());
self
}
}
#[derive(Debug, Clone)]
pub struct Body {
message: String,
}
impl Body {
pub fn new(message: impl Into<String>) -> Self {
Self {
message: message.into(),
}
}
fn to_xml(&self) -> String {
format!("<Body>{}</Body>", escape_xml_text(&self.message))
}
}
#[derive(Debug, Clone)]
pub struct Media {
url: String,
}
impl Media {
pub fn new(url: impl Into<String>) -> Self {
Self { url: url.into() }
}
fn to_xml(&self) -> String {
format!("<Media>{}</Media>", escape_xml_text(&self.url))
}
}
#[derive(Debug, Clone)]
pub struct Message {
attributes: MessageAttributes,
body: Option<Body>,
media: Vec<Media>,
}
impl Message {
pub(crate) fn new(attributes: MessageAttributes, body: Option<String>) -> Self {
Self {
attributes,
body: body.map(Body::new),
media: Vec::new(),
}
}
pub fn with_nouns(attributes: MessageAttributes) -> Self {
Self {
attributes,
body: None,
media: Vec::new(),
}
}
pub fn body(mut self, body: Body) -> Self {
self.body = Some(body);
self
}
pub fn add_media(mut self, media: Media) -> Self {
self.media.push(media);
self
}
pub fn media(mut self, media: Vec<Media>) -> Self {
self.media = media;
self
}
fn to_xml(&self) -> String {
let mut xml = String::from("<Message");
if let Some(ref action) = self.attributes.action {
xml.push_str(&format!(" action=\"{}\"", escape_xml_attr(action)));
}
if let Some(ref from) = self.attributes.from {
xml.push_str(&format!(" from=\"{}\"", escape_xml_attr(from)));
}
if let Some(ref method) = self.attributes.method {
xml.push_str(&format!(" method=\"{}\"", escape_xml_attr(method)));
}
if let Some(ref status_callback) = self.attributes.status_callback {
xml.push_str(&format!(
" statusCallback=\"{}\"",
escape_xml_attr(status_callback)
));
}
if let Some(ref to) = self.attributes.to {
xml.push_str(&format!(" to=\"{}\"", escape_xml_attr(to)));
}
if self.body.is_some() || !self.media.is_empty() {
xml.push('>');
if let Some(ref body) = self.body {
xml.push_str(&body.to_xml());
}
for media in &self.media {
xml.push_str(&media.to_xml());
}
xml.push_str("</Message>");
} else {
xml.push_str(" />");
}
xml
}
}
#[derive(Debug, Clone)]
pub struct Redirect {
attributes: RedirectAttributes,
url: String,
}
impl Redirect {
pub(crate) fn new(attributes: RedirectAttributes, url: impl Into<String>) -> Self {
Self {
attributes,
url: url.into(),
}
}
fn to_xml(&self) -> String {
let mut xml = String::from("<Redirect");
if let Some(ref method) = self.attributes.method {
xml.push_str(&format!(" method=\"{}\"", escape_xml_attr(method)));
}
xml.push_str(&format!(">{}</Redirect>", escape_xml_text(&self.url)));
xml
}
}
#[derive(Debug, Clone)]
pub(crate) enum MessagingVerb {
Message(Message),
Redirect(Redirect),
}
#[derive(Debug, Clone, Default)]
pub struct MessagingResponse {
pub(crate) verbs: Vec<MessagingVerb>,
comments_before: Vec<String>,
comments: Vec<String>,
comments_after: Vec<String>,
}
impl MessagingResponse {
pub fn new() -> Self {
Self::default()
}
pub fn message(mut self, body: impl Into<String>) -> Self {
let message = Message::new(MessageAttributes::default(), Some(body.into()));
self.verbs.push(MessagingVerb::Message(message));
self
}
pub fn message_with_attributes(
mut self,
attributes: MessageAttributes,
body: impl Into<String>,
) -> Self {
let message = Message::new(attributes, Some(body.into()));
self.verbs.push(MessagingVerb::Message(message));
self
}
pub fn message_with_nouns(mut self, message: Message) -> Self {
self.verbs.push(MessagingVerb::Message(message));
self
}
pub fn redirect(mut self, url: impl Into<String>) -> Self {
let redirect = Redirect::new(RedirectAttributes::default(), url);
self.verbs.push(MessagingVerb::Redirect(redirect));
self
}
pub fn redirect_with_attributes(
mut self,
attributes: RedirectAttributes,
url: impl Into<String>,
) -> Self {
let redirect = Redirect::new(attributes, url);
self.verbs.push(MessagingVerb::Redirect(redirect));
self
}
pub fn comment(mut self, comment: impl Into<String>) -> Self {
self.comments.push(comment.into());
self
}
pub fn comment_after(mut self, comment: impl Into<String>) -> Self {
self.comments_after.push(comment.into());
self
}
pub fn comment_before(mut self, comment: impl Into<String>) -> Self {
self.comments_before.push(comment.into());
self
}
}
impl TwiML for MessagingResponse {
fn to_xml(&self) -> String {
let mut xml = String::from("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
for comment in &self.comments_before {
xml.push_str(&format!("<!-- {} -->\n", escape_xml_text(comment)));
}
xml.push_str("<Response>");
for comment in &self.comments {
xml.push_str(&format!("\n <!-- {} -->", escape_xml_text(comment)));
}
for verb in &self.verbs {
match verb {
MessagingVerb::Message(message) => {
xml.push_str(&message.to_xml());
}
MessagingVerb::Redirect(redirect) => {
xml.push_str(&redirect.to_xml());
}
}
}
xml.push_str("</Response>");
for comment in &self.comments_after {
xml.push_str(&format!("\n<!-- {} -->", escape_xml_text(comment)));
}
xml
}
}