1mod call;
2mod message;
3pub mod twiml;
4mod webhook;
5
6pub use call::{Call, OutboundCall};
7use headers::authorization::{Authorization, Basic};
8use headers::{ContentType, HeaderMapExt};
9use hyper::client::connect::HttpConnector;
10use hyper::{Body, Method, StatusCode};
11use hyper_tls::HttpsConnector;
12pub use message::{Message, OutboundMessage};
13use std::collections::BTreeMap;
14use std::error::Error;
15use std::fmt::{self, Display, Formatter};
16use url::form_urlencoded;
17
18pub const GET: Method = Method::GET;
19pub const POST: Method = Method::POST;
20pub const PUT: Method = Method::PUT;
21
22#[derive(Clone)]
23pub struct Client {
24 account_id: String,
25 auth_token: String,
26 auth_header: Authorization<Basic>,
27 http_client: hyper::Client<HttpsConnector<HttpConnector>>,
28}
29
30fn url_encode(params: &[(&str, &str)]) -> String {
31 let mut url = form_urlencoded::Serializer::new(String::new());
32 for (k, v) in params {
33 url.append_pair(k, v);
34 }
35
36 url.finish()
37}
38
39#[derive(Debug)]
40pub enum TwilioError {
41 NetworkError(hyper::Error),
42 HTTPError(StatusCode),
43 ParsingError,
44 AuthError,
45 BadRequest,
46}
47
48impl Display for TwilioError {
49 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
50 match *self {
51 TwilioError::NetworkError(ref e) => e.fmt(f),
52 TwilioError::HTTPError(ref s) => write!(f, "Invalid HTTP status code: {}", s),
53 TwilioError::ParsingError => f.write_str("Parsing error"),
54 TwilioError::AuthError => f.write_str("Missing `X-Twilio-Signature` header in request"),
55 TwilioError::BadRequest => f.write_str("Bad request"),
56 }
57 }
58}
59
60impl Error for TwilioError {
61 fn source(&self) -> Option<&(dyn Error + 'static)> {
62 match *self {
63 TwilioError::NetworkError(ref e) => Some(e),
64 _ => None,
65 }
66 }
67}
68
69pub trait FromMap {
70 fn from_map(m: BTreeMap<String, String>) -> Result<Box<Self>, TwilioError>;
71}
72
73impl Client {
74 pub fn new(account_id: &str, auth_token: &str) -> Client {
75 Client {
76 account_id: account_id.to_string(),
77 auth_token: auth_token.to_string(),
78 auth_header: Authorization::basic(account_id, auth_token),
79 http_client: hyper::Client::builder().build(HttpsConnector::new()),
80 }
81 }
82
83 async fn send_request<T>(
84 &self,
85 method: hyper::Method,
86 endpoint: &str,
87 params: &[(&str, &str)],
88 ) -> Result<T, TwilioError>
89 where
90 T: serde::de::DeserializeOwned,
91 {
92 let url = format!(
93 "https://api.twilio.com/2010-04-01/Accounts/{}/{}.json",
94 self.account_id, endpoint
95 );
96 let mut req = hyper::Request::builder()
97 .method(method)
98 .uri(&*url)
99 .body(Body::from(url_encode(params)))
100 .unwrap();
101
102 let mime: mime::Mime = "application/x-www-form-urlencoded".parse().unwrap();
103 req.headers_mut().typed_insert(ContentType::from(mime));
104 req.headers_mut().typed_insert(self.auth_header.clone());
105
106 let resp = self
107 .http_client
108 .request(req)
109 .await
110 .map_err(TwilioError::NetworkError)?;
111
112 match resp.status() {
113 StatusCode::CREATED | StatusCode::OK => {}
114 other => return Err(TwilioError::HTTPError(other)),
115 };
116
117 let decoded: T = hyper::body::to_bytes(resp.into_body())
118 .await
119 .map_err(TwilioError::NetworkError)
120 .and_then(|bytes| {
121 serde_json::from_slice(&bytes).map_err(|_| TwilioError::ParsingError)
122 })?;
123
124 Ok(decoded)
125 }
126
127 pub async fn respond_to_webhook<T: FromMap, F>(
128 &self,
129 req: hyper::Request<Body>,
130 mut logic: F,
131 ) -> hyper::Response<Body>
132 where
133 F: FnMut(T) -> twiml::Twiml,
134 {
135 let o: T = match self.parse_request::<T>(req).await {
136 Ok(obj) => *obj,
137 Err(_) => {
138 let mut res = hyper::Response::new(Body::from("Error.".as_bytes()));
139 *res.status_mut() = StatusCode::BAD_REQUEST;
140 return res;
141 }
142 };
143
144 let t = logic(o);
145 let body = t.as_twiml();
146 let len = body.len() as u64;
147 let mut res = hyper::Response::new(Body::from(body));
148 res.headers_mut().typed_insert(headers::ContentType::xml());
149 res.headers_mut().typed_insert(headers::ContentLength(len));
150 res
151 }
152}