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

Issue:
V3 structures should be easier to construct 
Right now building a simple mail is so much boilerplate. 

- [ ] They should provide `with_xx` constructors
- [ ] They should allow for `let x = X::new().add_something().add_something_else()` (`add_something(mut self) -> Self`) 


Discussion / hints:
Again, this may fall in line with #30 so keep an eye out for that.

Fix patch:
diff --git a/.travis.yml b/.travis.yml
index fab28b1..caa7371 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -11,3 +11,5 @@ matrix:
 script:
     - cargo build
     - cargo test
+notifications:
+    email: false
diff --git a/Cargo.toml b/Cargo.toml
index 66f36ac..c45f566 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "sendgrid"
-version = "0.7.1"
+version = "0.8.0"
 authors = ["Garrett Squire <garrettsquire@gmail.com>"]
 description = "An unofficial client library for the SendGrid API"
 repository = "https://github.com/gsquire/sendgrid-rs"
@@ -11,7 +11,7 @@ documentation = "https://docs.rs/sendgrid"
 readme = "README.md"
 
 [dependencies]
-data-encoding = "2.0"
+data-encoding = "2.1"
 reqwest = "0.9"
 serde = "1.0"
 serde_derive = "1.0"
diff --git a/examples/v3.rs b/examples/v3.rs
index b2b79a0..745720b 100644
--- a/examples/v3.rs
+++ b/examples/v3.rs
@@ -5,31 +5,27 @@ use std::collections::HashMap;
 use sendgrid::v3::*;
 
 fn main() {
-    let mut m = SGMailV3::new();
-
-    let mut e = Email::new();
-    e.set_email("g@gmail.com");
-
-    let mut c = Content::new();
-    c.set_content_type("text/html");
-    c.set_value("Test");
-
-    let mut p = Personalization::new();
-    p.add_to(e.clone());
-
     let mut cool_header = HashMap::new();
     cool_header.insert(String::from("x-cool"), String::from("indeed"));
     cool_header.insert(String::from("x-cooler"), String::from("cold"));
-    p.add_headers(cool_header);
 
-    m.set_from(e.clone());
-    m.set_subject("Subject");
-    m.add_content(c);
-    m.add_personalization(p);
+    let p = Personalization::new()
+        .add_to(Email::new().set_email("test@example.com"))
+        .add_headers(cool_header);
+
+    let m = Message::new()
+        .set_from(Email::new().set_email("g@gmail.com"))
+        .set_subject("Subject")
+        .add_content(
+            Content::new()
+                .set_content_type("text/html")
+                .set_value("Test"),
+        )
+        .add_personalization(p);
 
     let mut env_vars = ::std::env::vars();
     let api_key = env_vars.find(|v| v.0 == "SG_API_KEY").unwrap();
-    let sender = V3Sender::new(api_key.1);
+    let sender = Sender::new(api_key.1);
     let code = sender.send(&m);
     println!("{:?}", code);
 }
diff --git a/src/v3.rs b/src/v3.rs
index 43c3b6e..7251a81 100644
--- a/src/v3.rs
+++ b/src/v3.rs
@@ -1,3 +1,5 @@
+//! This module encompasses all types needed to send mail using version 3 of the mail
+//! send API.
 use std::collections::HashMap;
 
 use data_encoding::BASE64;
@@ -15,14 +17,14 @@ const V3_API_URL: &str = "https://api.sendgrid.com/v3/mail/send";
 pub type SGMap = HashMap<String, String>;
 
 /// Used to send a V3 message body.
-pub struct V3Sender {
+pub struct Sender {
     api_key: String,
 }
 
 /// The main structure for a V3 API mail send call. This is composed of many other smaller
 /// structures used to add lots of customization to your message.
 #[derive(Default, Serialize)]
-pub struct SGMailV3 {
+pub struct Message {
     from: Email,
     subject: String,
     personalizations: Vec<Personalization>,
@@ -104,14 +106,14 @@ pub struct Attachment {
     content_id: Option<String>,
 }
 
-impl V3Sender {
+impl Sender {
     /// Construct a new V3 message sender.
-    pub fn new(api_key: String) -> V3Sender {
-        V3Sender { api_key }
+    pub fn new(api_key: String) -> Sender {
+        Sender { api_key }
     }
 
     /// Send a V3 message and return the status code or an error from the request.
-    pub fn send(&self, mail: &SGMailV3) -> SendgridResult<Response> {
+    pub fn send(&self, mail: &Message) -> SendgridResult<Response> {
         let client = Client::new();
         let mut headers = HeaderMap::new();
         headers.insert(
@@ -130,29 +132,32 @@ impl V3Sender {
     }
 }
 
-impl SGMailV3 {
+impl Message {
     /// Construct a new V3 message.
-    pub fn new() -> SGMailV3 {
-        SGMailV3::default()
+    pub fn new() -> Message {
+        Message::default()
     }
 
     /// Set the from address.
-    pub fn set_from(&mut self, from: Email) {
+    pub fn set_from(mut self, from: Email) -> Message {
         self.from = from;
+        self
     }
 
     /// Set the subject.
-    pub fn set_subject(&mut self, subject: &str) {
+    pub fn set_subject(mut self, subject: &str) -> Message {
         self.subject = String::from(subject);
+        self
     }
 
     /// Set the template id.
-    pub fn set_template_id(&mut self, template_id: &str) {
+    pub fn set_template_id(mut self, template_id: &str) -> Message {
         self.template_id = Some(String::from(template_id));
+        self
     }
 
     /// Add content to the message.
-    pub fn add_content(&mut self, c: Content) {
+    pub fn add_content(mut self, c: Content) -> Message {
         match self.content {
             None => {
                 let mut content = Vec::new();
@@ -161,15 +166,17 @@ impl SGMailV3 {
             }
             Some(ref mut content) => content.push(c),
         };
+        self
     }
 
     /// Add a personalization to the message.
-    pub fn add_personalization(&mut self, p: Personalization) {
+    pub fn add_personalization(mut self, p: Personalization) -> Message {
         self.personalizations.push(p);
+        self
     }
 
     /// Add an attachment to the message.
-    pub fn add_attachment(&mut self, a: Attachment) {
+    pub fn add_attachment(mut self, a: Attachment) -> Message {
         match self.attachments {
             None => {
                 let mut attachments = Vec::new();
@@ -178,6 +185,7 @@ impl SGMailV3 {
             }
             Some(ref mut attachments) => attachments.push(a),
         };
+        self
     }
 
     fn gen_json(&self) -> String {
@@ -192,13 +200,15 @@ impl Email {
     }
 
     /// Set the address for this email.
-    pub fn set_email(&mut self, email: &str) {
+    pub fn set_email(mut self, email: &str) -> Email {
         self.email = String::from(email);
+        self
     }
 
     /// Set an optional name.
-    pub fn set_name(&mut self, name: &str) {
+    pub fn set_name(mut self, name: &str) -> Email {
         self.name = Some(String::from(name));
+        self
     }
 }
 
@@ -209,13 +219,15 @@ impl Content {
     }
 
     /// Set the type of this content.
-    pub fn set_content_type(&mut self, content_type: &str) {
+    pub fn set_content_type(mut self, content_type: &str) -> Content {
         self.content_type = String::from(content_type);
+        self
     }
 
     /// Set the corresponding message for this content.
-    pub fn set_value(&mut self, value: &str) {
+    pub fn set_value(mut self, value: &str) -> Content {
         self.value = String::from(value);
+        self
     }
 }
 
@@ -226,12 +238,13 @@ impl Personalization {
     }
 
     /// Add a to field.
-    pub fn add_to(&mut self, to: Email) {
+    pub fn add_to(mut self, to: Email) -> Personalization {
         self.to.push(to);
+        self
     }
 
     /// Add a CC field.
-    pub fn add_cc(&mut self, cc: Email) {
+    pub fn add_cc(mut self, cc: Email) -> Personalization {
         match self.cc {
             None => {
                 let mut ccs = Vec::new();
@@ -242,10 +255,11 @@ impl Personalization {
                 c.push(cc);
             }
         }
+        self
     }
 
     /// Add a BCC field.
-    pub fn add_bcc(&mut self, bcc: Email) {
+    pub fn add_bcc(mut self, bcc: Email) -> Personalization {
         match self.bcc {
             None => {
                 let mut bccs = Vec::new();
@@ -256,10 +270,11 @@ impl Personalization {
                 b.push(bcc);
             }
         }
+        self
     }
 
     /// Add a headers field.
-    pub fn add_headers(&mut self, headers: SGMap) {
+    pub fn add_headers(mut self, headers: SGMap) -> Personalization {
         match self.headers {
             None => {
                 let mut h = HashMap::new();
@@ -272,10 +287,11 @@ impl Personalization {
                 h.extend(headers);
             }
         }
+        self
     }
 
     /// Add a dynamic template data field.
-    pub fn add_dynamic_template_data(&mut self, dynamic_template_data: SGMap) {
+    pub fn add_dynamic_template_data(mut self, dynamic_template_data: SGMap) -> Personalization {
         match self.dynamic_template_data {
             None => {
                 let mut h = HashMap::new();
@@ -288,6 +304,7 @@ impl Personalization {
                 h.extend(dynamic_template_data);
             }
         }
+        self
     }
 }
 
@@ -298,23 +315,27 @@ impl Attachment {
     }
 
     /// The raw body of the attachment.
-    pub fn set_content(&mut self, c: &[u8]) {
+    pub fn set_content(mut self, c: &[u8]) -> Attachment {
         self.content = BASE64.encode(c);
+        self
     }
 
     /// The base64 body of the attachment.
-    pub fn set_base64_content(&mut self, c: &str) {
+    pub fn set_base64_content(mut self, c: &str) -> Attachment {
         self.content = String::from(c);
+        self
     }
 
     /// Sets the filename for the attachment.
-    pub fn set_filename(&mut self, filename: &str) {
+    pub fn set_filename(mut self, filename: &str) -> Attachment {
         self.filename = filename.into();
+        self
     }
 
     /// Set an optional mime type. Sendgrid will default to 'application/octet-stream'
     /// if unspecified.
-    pub fn set_mime_type(&mut self, mime: &str) {
+    pub fn set_mime_type(mut self, mime: &str) -> Attachment {
         self.mime_type = Some(String::from(mime));
+        self
     }
 }


Test patch: