Repository: gsquire/sendgrid-rs (language: rust)

Issue:
form_encoder
@gsquire 

What do you think about using this library for the building of the form encoded data during creation of the message body?

[dependencies]
url = "1.0.0"

```
extern crate url;
use url::form_urlencoded;

fn main() {
    let encoded: String = form_urlencoded::Serializer::new(String::new())
          .append_pair("to[]", "bob@example.com")
          .append_pair("saison", "Été+hiver")
          .finish();
    println!("{:?}", encoded);
}
```

Output: to%5B%5D=bob%40example.com&saison=%C3%89t%C3%A9%2Bhiver



Discussion / hints:
Actually, now that I think about this...using a library like this would be redundant with the underlying library that's making the post.  Which I assumes converts the body into form encoded data or it wouldn't work properly.  

I'm trying to see if there's a way to get rid all the string concatenation happening.

I don't think hyper will actually encode the post body leaving it up to the user. So I think this library will work well especially for special characters. If you want to open a PR, that would be sweet.

Cool I'll try to get something started.


Fix patch:
diff --git a/Cargo.toml b/Cargo.toml
index fd3aea0..57f8a49 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "sendgrid"
-version = "0.2.2"
+version = "0.3.0"
 authors = ["Garrett Squire <garrettsquire@gmail.com>"]
 description = "An unofficial client library for the SendGrid API"
 repository = "https://github.com/gsquire/sendgrid-rs"
@@ -11,8 +11,8 @@ documentation = "https://gsquire.github.io/doc/sendgrid/sendgrid"
 
 [dependencies]
 hyper = "0.9"
-time = "0.1"
 rustc-serialize = "0.3"
+url = "1.1.1"
 
 [[bin]]
 name = "main"
diff --git a/README.md b/README.md
index 7082b5b..8249d00 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@ sendgrid-rs is available on [crates.io](https://crates.io/crates/sendgrid) and c
 
 ```toml
 [dependencies]
-sendgrid = "0.2"
+sendgrid = "0.3"
 ```
 
 ## Build Dependencies
diff --git a/examples/main.rs b/examples/main.rs
index 2f4b2aa..fe66e94 100644
--- a/examples/main.rs
+++ b/examples/main.rs
@@ -1,5 +1,4 @@
 extern crate sendgrid;
-extern crate time;
 
 use sendgrid::mail::Mail;
 use sendgrid::sg_client::SGClient;
diff --git a/src/lib.rs b/src/lib.rs
index 1536eec..7b3c78b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,5 +1,6 @@
 extern crate hyper;
 extern crate rustc_serialize;
+extern crate url;
 
 pub mod sg_client;
 pub mod mail;
diff --git a/src/sg_client/mod.rs b/src/sg_client/mod.rs
index c0db7a8..a9dcf5d 100644
--- a/src/sg_client/mod.rs
+++ b/src/sg_client/mod.rs
@@ -1,6 +1,5 @@
 use mail::Mail;
 
-use std::borrow::Cow;
 use std::io::Read;
 
 use hyper::Client;
@@ -8,6 +7,8 @@ use hyper::error::Error;
 use hyper::header::{Authorization, Bearer, ContentType, Headers, UserAgent};
 use hyper::mime::{Mime, TopLevel, SubLevel};
 
+use url::form_urlencoded::Serializer;
+
 static API_URL: &'static str = "https://api.sendgrid.com/api/mail.send.json?";
 
 /// This is the struct that allows you to authenticate to the SendGrid API.
@@ -16,77 +17,57 @@ pub struct SGClient {
     api_key: String,
 }
 
-fn make_post_body<'a>(mut mail_info: Mail) -> Cow<'a, str> {
-    let mut body = String::new();
+// Given a form value and a key, generate the correct key.
+fn make_form_key(form: &str, key: &str) -> String {
+    let mut value = String::new();
+    value.push_str(form);
+    value.push('[');
+    value.push_str(key);
+    value.push(']');
+
+    value
+}
 
-    // The leading POST data should not start with an ampersand.
-    let first_to = mail_info.to.remove(0);
-    body.push_str("to[]=");
-    body.push_str(&first_to[..]);
+// Use the URL form encoder to properly generate the body used in the mail send request.
+fn make_post_body(mut mail_info: Mail) -> String {
+    let body = String::new();
+    let mut encoder = Serializer::new(body);
 
-    // Now, add anymore if need be.
     for to in mail_info.to.iter() {
-        body.push_str("&to[]=");
-        body.push_str(&to[..]);
+        encoder.append_pair("to[]", &to);
     }
 
     for to_name in mail_info.to_names.iter() {
-        body.push_str("&toname[]=");
-        body.push_str(&to_name[..]);
+        encoder.append_pair("toname[]", &to_name);
     }
 
     for cc in mail_info.cc.iter() {
-        body.push_str("&cc[]=");
-        body.push_str(&cc[..]);
+        encoder.append_pair("cc[]", &cc);
     }
 
     for bcc in mail_info.bcc.iter() {
-        body.push_str("&bcc[]=");
-        body.push_str(&bcc[..]);
+        encoder.append_pair("bcc[]", &bcc);
     }
 
     for (attachment, contents) in &mail_info.attachments {
-        body.push_str("&files[");
-        body.push_str(attachment);
-        body.push_str("]=");
-        body.push_str(contents);
+        encoder.append_pair(&make_form_key("files", attachment), contents);
     }
 
     for (id, value) in &mail_info.content {
-        body.push_str("&content[");
-        body.push_str(id);
-        body.push_str("]=");
-        body.push_str(value);
+        encoder.append_pair(&make_form_key("content", id), value);
     }
 
-    body.push_str("&from=");
-    body.push_str(&mail_info.from);
-
-    body.push_str("&subject=");
-    body.push_str(&mail_info.subject);
-
-    body.push_str("&html=");
-    body.push_str(&mail_info.html);
-
-    body.push_str("&text=");
-    body.push_str(&mail_info.text);
-
-    body.push_str("&fromname=");
-    body.push_str(&mail_info.from_name);
-
-    body.push_str("&replyto=");
-    body.push_str(&mail_info.reply_to);
-
-    body.push_str("&date=");
-    body.push_str(&mail_info.date[..]);
-
-    body.push_str("&headers=");
-    body.push_str(&mail_info.make_header_string()[..]);
-
-    body.push_str("&x-smtpapi=");
-    body.push_str(&mail_info.x_smtpapi[..]);
-
-    body.into()
+    encoder.append_pair("from", &mail_info.from);
+    encoder.append_pair("subject", &mail_info.subject);
+    encoder.append_pair("html", &mail_info.html);
+    encoder.append_pair("text", &mail_info.text);
+    encoder.append_pair("fromname", &mail_info.from_name);
+    encoder.append_pair("replyto", &mail_info.reply_to);
+    encoder.append_pair("date", &mail_info.date);
+    encoder.append_pair("headers", &mail_info.make_header_string());
+    encoder.append_pair("x-smtpapi", &mail_info.x_smtpapi);
+
+    encoder.finish()
 }
 
 impl SGClient {
@@ -115,10 +96,10 @@ impl SGClient {
             UserAgent("sendgrid-rs".to_owned())
         );
 
-        let post_body = make_post_body(mail_info).into_owned();
+        let post_body = make_post_body(mail_info);
         let mut res = try!(client.post(API_URL)
             .headers(headers)
-            .body(&post_body[..])
+            .body(&post_body)
             .send());
         let mut body = String::new();
         try!(res.read_to_string(&mut body));
@@ -135,7 +116,14 @@ fn basic_message_body() {
     m.add_text("It works");
 
     let body = make_post_body(m);
-    let comparison = "to[]=test@example.com&from=me@example.com&subject=Test\
-        &html=&text=It works&fromname=&replyto=&date=&headers={}&x-smtpapi=";
-    assert_eq!(body, comparison);
+    let want = "to%5B%5D=test%40example.com&from=me%40example.com&subject=Test&\
+                html=&text=It+works&fromname=&replyto=&date=&headers=%7B%7D&x-smtpapi=";
+    assert_eq!(body, want);
+}
+
+#[test]
+fn test_proper_key() {
+    let want = "files[test.jpg]";
+    let got = make_form_key("files", "test.jpg");
+    assert_eq!(want, got);
 }


Test patch: