Repository: driftluo/InfluxDBClient-rs (language: rust)

Issue:
Disabling Authentication
It seems that you assume that Influxdb authentication mechanism will always be enabled, but it also can be disabled, and there is no way no to passe credentials, or even the ability to not pass the "u" and "p" arguments in the url.

Do you have any plans in supporting disabled authentication?


Discussion / hints:
Influxdb can disabled the authentication mechanism，but when I test on 1.01 version，even if disabled the authentication mechanism, push the user name and password will not be a problem, it seems to be directly ignored.

Does the current version fix this problem? If so, I will change it on my project.
Well that was just that i expected username and password Option\<T> types, but that's no big issue
Next version, maybe 0.3.0, I want to support https, the Influxdbclient maybe like this:
```
struct InfluxdbClient {
    host: String,
    db: String,
    ssl: bool,
    username: Option<String>,
    passwd: Option<String>,
    read_timeout: Option<u64>,
    write_timeout: Option<u64>,
}
```
and user only need to provide such as `localhost:8086` but not `http://localhost:8086`.

I also want to support udp client(but it seems that there is little need for it) .

And I would like to refactor the code, it seems a bit messy now.

emm, but when to start doing this is a problem. : (

Fix patch:
diff --git a/Cargo.toml b/Cargo.toml
index 7f3cfdc..0d96a92 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "influx_db_client"
-version = "0.2.5"
+version = "0.3.0"
 authors = ["piaoliu <441594700@qq.com>"]
 documentation = "https://docs.rs/influx_db_client/"
 repository = "https://github.com/driftluo/InfluxDBClient-rs"
@@ -10,5 +10,11 @@ keywords = ["influxdb"]
 license = "MIT"
 
 [dependencies]
-hyper = "~0.10.12"
-serde_json = '~1.0.2'
+hyper = "^0.10.13"
+hyper-native-tls = "^0.2.4"
+serde_json = '^1.0.2'
+serde_derive = "^1.0.15"
+serde = "^1.0.15"
+
+[dev-dependencies]
+native-tls = "^0.1.4"
diff --git a/README.md b/README.md
index 1e31e9b..93f468b 100644
--- a/README.md
+++ b/README.md
@@ -12,78 +12,70 @@ This is an InfluxDB driver for Rust.
 
 This project has been able to run properly, PR is welcome.
 
-### Todo
+## Usage
 
-- [x] Multiple write and single write.
-- [x] Get the version number and check if the database exists.
-- [x] Support query syntax.
-- [x] Support query post syntax.
-- [x] Add some easy-to-use functions.
-- [x] Add the type of error
+### Recommend
 
-## Usage
+```
+[dependencies]
+influx_db_client = "^0.3.0"
+
+[patch.crates-io]
+influx_db_client = { git = 'https://github.com/driftluo/InfluxDBClient-rs' }
+```
 
-### Single thread
+### http
 
 ```
+#[macro_use]
 extern crate influx_db_client;
 
-use influx_db_client::{InfluxdbClient, Point, Points, Value, InfluxClient, Precision};
+use influx_db_client::{Client, Point, Points, Value, Precision};
 
 fn main() {
-    let mut client = InfluxdbClient::new("http://localhost:8086", "test", "root", "root");
-    client.set_write_timeout(10);
-    client.set_read_timeout(10);
+    // default with "http://127.0.0.1:8086", db with "test"
+    let client = Client::default().set_authentication("root", "root");
 
-    let mut point = Point::new("test");
-    point.add_field("somefield", Value::Integer(65));
+    let mut point = point!("test1");
+    point.add_field("foo", Value::String("bar".to_string()));
+    point.add_field("integer", Value::Integer(11));
+    point.add_field("float", Value::Float(22.3));
+    point.add_field("'boolean'", Value::Boolean(false));
 
-    let mut point1 = Point::new("test2");
-    point1.add_field("somefield", Value::Float(12.2));
-    point1.add_tag("sometag", Value::Boolean(false));
+    let mut point1 = Point::new("test1");
+    point1.add_tag("tags", Value::String(String::from("'=213w")));
+    point1.add_tag("number", Value::Integer(12));
+    point1.add_tag("float", Value::Float(12.6));
+    point1.add_field("fd", Value::String("'3'".to_string()));
 
-    let mut points = Points::new(point);
-    points.push(point1);
+    let points = points!(point1, point);
 
     // if Precision is None, the default is second
     // Multiple write
-    let res = client.write_points(points, Some(Precision::Microseconds), None).unwrap();
-    let version = client.get_version().unwrap();
-    println!("{}\nversion:{}", res, version)
+    let _ = client.write_points(points, Some(Precision::Seconds), None).unwrap();
 
-    // query
-    let res = client.query("select * from test", None).unwrap();
-    println!("{:?}", res[0].get("series").unwrap()[0].get("values"))
+    // query, it's type is Option<Vec<Node>>
+    let res = client.query("select * from test1", None).unwrap();
+    println!("{:?}", res.unwrap()[0].series)
 }
 ```
 
-### Multi-threaded
-
-You can use the smart pointer, such as Arc, Mutex, Refcell, to wrap the influxdbclient, or move the client into the thread as shown below.
+### udp
 
-```
+```Rust
+#[macro_use]
 extern crate influx_db_client;
 
-use influx_db_client::{InfluxdbClient, Point, Value, InfluxClient};
-use std::thread;
+use influx_db_client::{UdpClient, Point, Value};
 
-#[allow(unused_must_use)]
 fn main() {
-    let mut client = InfluxdbClient::new("http://localhost:8086", "test", "root", "root");
-    client.set_write_timeout(10);
-    client.set_read_timeout(10);
-    let client1 = client.clone();
-
-    let handle = thread::spawn(move || {
-        let mut point = Point::new("test");
-        point.add_field("'somefield'", Value::String(String::from("1")));
-        client1.write_point(point, None, None).unwrap();
-    });
+    let mut udp = UdpClient::new("127.0.0.1:8089");
+    udp.add_host("127.0.0.1:8090");
 
-    handle.join();
+    let mut point = point!("test");
+    point.add_field("foo", Value::String(String::from("bar")));
 
-    let res = client.query("select * from test", None).unwrap();
-    println!("{:?}", res[0].get("series").unwrap()[0].get("values"))
+    let _ = udp.write_point(point).unwrap();
 }
 ```
 
diff --git a/src/client.rs b/src/client.rs
new file mode 100644
index 0000000..ae9bd04
--- /dev/null
+++ b/src/client.rs
@@ -0,0 +1,513 @@
+use serde_json;
+use hyper::client::Client as hyper_client;
+use hyper::net::HttpsConnector;
+use hyper::client::{ RequestBuilder, };
+use hyper_native_tls::native_tls::TlsConnector;
+use hyper_native_tls::NativeTlsClient;
+use hyper::Url;
+use std::io::Read;
+use std::time::Duration;
+use std::net::UdpSocket;
+use std::iter::FromIterator;
+use std::net::{ ToSocketAddrs, SocketAddr };
+use { error, serialization, Precision, Point, Points, Node, Query };
+
+/// The client to influxdb
+#[derive(Debug)]
+pub struct Client {
+    host: String,
+    db: String,
+    authentication: Option<(String, String)>,
+    client: HttpClient
+}
+
+unsafe impl Send for Client {}
+
+impl Client {
+    /// Create a new influxdb client with http
+    pub fn new<T>(host: T, db: T) -> Self
+        where T: ToString {
+        Client {
+            host: host.to_string(),
+            db: db.to_string(),
+            authentication: None,
+            client: HttpClient::default()
+        }
+    }
+
+    /// Create a new influxdb client with https
+    pub fn new_with_option<T: ToString>(host: T, db: T, tls_option: Option<TLSOption>) -> Self {
+        Client {
+            host: host.to_string(),
+            db: db.to_string(),
+            authentication: None,
+            client: HttpClient::new_with_option(tls_option)
+        }
+    }
+
+    /// Set the read timeout value, unit "s"
+    pub fn set_read_timeout(&mut self, timeout: u64) {
+        self.client.set_read_timeout(Duration::from_secs(timeout));
+    }
+
+    /// Set the write timeout value, unit "s"
+    pub fn set_write_timeout(&mut self, timeout: u64) {
+        self.client.set_write_timeout(Duration::from_secs(timeout));
+    }
+
+    /// Change the client's database
+    pub fn swith_database<T>(&mut self, database: T) where T: ToString {
+        self.db = database.to_string();
+    }
+
+    /// Change the client's user
+    pub fn set_authentication<T>(mut self, user: T, passwd: T) -> Self where T: Into<String> {
+        self.authentication = Some((user.into(), passwd.into()));
+        self
+    }
+
+    /// Change http to https, but don't leave the read write timeout setting
+    pub fn set_tls(mut self, connector: Option<TLSOption>) -> Self {
+        self.client = HttpClient::new_with_option(connector);
+        self
+    }
+
+    /// View the current db name
+    pub fn get_db(&self) -> String {
+        self.db.to_owned()
+    }
+
+    /// Query whether the corresponding database exists, return bool
+    pub fn ping(&self) -> bool {
+        let url = self.build_url("ping", None);
+        let res = self.client.get(url).send().unwrap();
+        match res.status_raw().0 {
+            204 => true,
+            _ => false,
+        }
+    }
+
+    /// Query the version of the database and return the version number
+    pub fn get_version(&self) -> Option<String> {
+        let url = self.build_url("ping", None);
+        let res = self.client.get(url).send().unwrap();
+        match res.status_raw().0 {
+            204 => match res.headers.get_raw("X-Influxdb-Version") {
+                Some(i) => Some(String::from_utf8(i[0].to_vec()).unwrap()),
+                None => Some(String::from("Don't know"))
+            },
+            _ => None,
+        }
+    }
+
+    /// Write a point to the database
+    pub fn write_point(&self, point: Point, precision: Option<Precision>, rp: Option<&str>) -> Result<(), error::Error> {
+        let points = Points::new(point);
+        self.write_points(points, precision, rp)
+    }
+
+    /// Write multiple points to the database
+    pub fn write_points(&self, points: Points, precision: Option<Precision>, rp: Option<&str>) -> Result<(), error::Error> {
+        let line = serialization::line_serialization(points);
+
+        let mut param = vec![("db", self.db.as_str())];
+
+        match precision {
+            Some(ref t) => param.push(("precision", t.to_str())),
+            None => param.push(("precision", "s")),
+        };
+
+        match rp {
+            Some(t) => param.push(("rp", t)),
+            None => (),
+        };
+
+        let url = self.build_url("write", Some(param));
+
+        let mut res = self.client.post(url).body(&line).send()?;
+        let mut err = String::new();
+        let _ = res.read_to_string(&mut err);
+
+        match res.status_raw().0 {
+            204 => Ok(()),
+            400 => Err(error::Error::SyntaxError(serialization::conversion(err))),
+            401 | 403 => Err(error::Error::InvalidCredentials("Invalid authentication credentials.".to_string())),
+            404 => Err(error::Error::DataBaseDoesNotExist(serialization::conversion(err))),
+            500 => Err(error::Error::RetentionPolicyDoesNotExist(err)),
+            _ => Err(error::Error::Unknow("There is something wrong".to_string()))
+        }
+    }
+
+    /// Query and return data, the data type is `Vec<serde_json::Value>`
+    pub fn query(&self, q: &str, epoch: Option<Precision>) -> Result<Option<Vec<Node>>, error::Error> {
+        match self.query_raw(q, epoch) {
+            Ok(t) => Ok(t.results),
+            Err(e) => Err(e)
+        }
+    }
+
+    /// Drop measurement
+    pub fn drop_measurement(&self, measurement: &str) -> Result<(), error::Error> {
+        let sql = format!("Drop measurement {}", serialization::quote_ident(measurement));
+
+        match self.query_raw(&sql, None) {
+            Ok(_) => Ok(()),
+            Err(e) => Err(e)
+        }
+    }
+
+    /// Create a new database in InfluxDB.
+    pub fn create_database(&self, dbname: &str) -> Result<(), error::Error> {
+        let sql = format!("Create database {}", serialization::quote_ident(dbname));
+
+        match self.query_raw(&sql, None) {
+            Ok(_) => Ok(()),
+            Err(e) => Err(e)
+        }
+    }
+
+    /// Drop a database from InfluxDB.
+    pub fn drop_database(&self, dbname: &str) -> Result<(), error::Error> {
+        let sql = format!("Drop database {}", serialization::quote_ident(dbname));
+
+        match self.query_raw(&sql, None) {
+            Ok(_) => Ok(()),
+            Err(e) => Err(e)
+        }
+    }
+
+    /// Create a new user in InfluxDB.
+    pub fn create_user(&self, user: &str, passwd: &str, admin: bool) -> Result<(), error::Error> {
+        let sql: String = {
+            if admin {
+                format!("Create user {0} with password {1} with all privileges",
+                        serialization::quote_ident(user), serialization::quote_literal(passwd))
+            } else {
+                format!("Create user {0} WITH password {1}", serialization::quote_ident(user),
+                        serialization::quote_literal(passwd))
+            }
+        };
+
+        match self.query_raw(&sql, None) {
+            Ok(_) => Ok(()),
+            Err(e) => Err(e)
+        }
+    }
+
+    /// Drop a user from InfluxDB.
+    pub fn drop_user(&self, user: &str) -> Result<(), error::Error> {
+        let sql = format!("Drop user {}", serialization::quote_ident(user));
+
+        match self.query_raw(&sql, None) {
+            Ok(_) => Ok(()),
+            Err(e) => Err(e)
+        }
+    }
+
+    /// Change the password of an existing user.
+    pub fn set_user_password(&self, user: &str, passwd: &str) -> Result<(), error::Error> {
+        let sql = format!("Set password for {}={}", serialization::quote_ident(user),
+                          serialization::quote_literal(passwd));
+
+        match self.query_raw(&sql, None) {
+            Ok(_) => Ok(()),
+            Err(e) => Err(e)
+        }
+    }
+
+    /// Grant cluster administration privileges to a user.
+    pub fn grant_admin_privileges(&self, user: &str) -> Result<(), error::Error> {
+        let sql = format!("Grant all privileges to {}", serialization::quote_ident(user));
+
+        match self.query_raw(&sql, None) {
+            Ok(_) => Ok(()),
+            Err(e) => Err(e)
+        }
+    }
+
+    /// Revoke cluster administration privileges from a user.
+    pub fn revoke_admin_privileges(&self, user: &str) -> Result<(), error::Error> {
+        let sql = format!("Revoke all privileges from {}", serialization::quote_ident(user));
+
+        match self.query_raw(&sql, None) {
+            Ok(_) => Ok(()),
+            Err(e) => Err(e)
+        }
+    }
+
+    /// Grant a privilege on a database to a user.
+    /// :param privilege: the privilege to grant, one of 'read', 'write'
+    /// or 'all'. The string is case-insensitive
+    pub fn grant_privilege(&self, user: &str, db: &str, privilege: &str) -> Result<(), error::Error> {
+        let sql = format!("Grant {} on {} to {}", privilege, serialization::quote_ident(db),
+                          serialization::quote_ident(user));
+
+        match self.query_raw(&sql, None) {
+            Ok(_) => Ok(()),
+            Err(e) => Err(e)
+        }
+    }
+
+    /// Revoke a privilege on a database from a user.
+    /// :param privilege: the privilege to grant, one of 'read', 'write'
+    /// or 'all'. The string is case-insensitive
+    pub fn revoke_privilege(&self, user: &str, db: &str, privilege: &str) -> Result<(), error::Error> {
+        let sql = format!("Revoke {0} on {1} from {2}", privilege, serialization::quote_ident(db),
+                          serialization::quote_ident(user));
+
+        match self.query_raw(&sql, None) {
+            Ok(_) => Ok(()),
+            Err(e) => Err(e)
+        }
+    }
+
+    /// Create a retention policy for a database.
+    /// :param duration: the duration of the new retention policy.
+    ///  Durations such as 1h, 90m, 12h, 7d, and 4w, are all supported
+    ///  and mean 1 hour, 90 minutes, 12 hours, 7 day, and 4 weeks,
+    ///  respectively. For infinite retention – meaning the data will
+    ///  never be deleted – use 'INF' for duration.
+    ///  The minimum retention period is 1 hour.
+    pub fn create_retention_policy(&self, name: &str, duration: &str, replication: &str, default: bool, db: Option<&str>) -> Result<(), error::Error> {
+        let database = {
+            if let Some(t) = db {
+                t
+            } else {
+                &self.db
+            }
+        };
+
+        let sql: String = {
+            if default {
+                format!("Create retention policy {} on {} duration {} replication {} default",
+                        serialization::quote_ident(name), serialization::quote_ident(database), duration, replication)
+            } else {
+                format!("Create retention policy {} on {} duration {} replication {}",
+                        serialization::quote_ident(name), serialization::quote_ident(database), duration, replication)
+            }
+        };
+
+        match self.query_raw(&sql, None) {
+            Ok(_) => Ok(()),
+            Err(e) => Err(e)
+        }
+    }
+
+    /// Drop an existing retention policy for a database.
+    pub fn drop_retention_policy(&self, name: &str, db: Option<&str>) -> Result<(), error::Error> {
+        let database = {
+            if let Some(t) = db {
+                t
+            } else {
+                &self.db
+            }
+        };
+
+        let sql = format!("Drop retention policy {} on {}", serialization::quote_ident(name),
+                          serialization::quote_ident(database));
+
+        match self.query_raw(&sql, None) {
+            Ok(_) => Ok(()),
+            Err(e) => Err(e)
+        }
+    }
+
+    /// Query and return to the native json structure
+    fn query_raw(&self, q: &str, epoch: Option<Precision>) -> Result<Query, error::Error> {
+        let mut param = vec![("db", self.db.as_str()), ("q", q)];
+
+        match epoch {
+            Some(ref t) => param.push(("epoch", t.to_str())),
+            None => ()
+        }
+
+        let url = self.build_url("query", Some(param));
+
+        let q_lower = q.to_lowercase();
+        let mut res = {
+            if q_lower.starts_with("select") && !q_lower.contains("into") || q_lower.starts_with("show") {
+                self.client.get(url).send()?
+            } else {
+                self.client.post(url).send()?
+            }
+        };
+
+        let mut context = String::new();
+        let _ = res.read_to_string(&mut context);
+
+        let json_data: Query = serde_json::from_str(&context).unwrap();
+
+        match res.status_raw().0 {
+            200 => Ok(json_data),
+            400 => Err(error::Error::SyntaxError(serialization::conversion(json_data.error.unwrap()))),
+            401 | 403 => Err(error::Error::InvalidCredentials("Invalid authentication credentials.".to_string())),
+            _ => Err(error::Error::Unknow("There is something wrong".to_string()))
+        }
+    }
+
+    /// Constructs the full URL for an API call.
+    fn build_url(&self, key: &str, param: Option<Vec<(&str, &str)>>) -> Url {
+        let url = Url::parse(&self.host).unwrap().join(key).unwrap();
+
+        let mut authentication = Vec::new();
+
+        match self.authentication {
+            Some(ref t) => {
+                authentication.push(("u", &t.0));
+                authentication.push(("p", &t.1));
+            }
+            None => {}
+        }
+
+        let url = Url::parse_with_params(url.as_str(), authentication).unwrap();
+
+        if param.is_some() {
+            Url::parse_with_params(url.as_str(), param.unwrap()).unwrap()
+        } else {
+            url
+        }
+    }
+}
+
+impl Default for Client {
+    /// connecting for default database `test` and host `http://localhost:8086`
+    fn default() -> Self {
+        Client::new("http://localhost:8086", "test")
+    }
+}
+
+/// Option for configuring the behavior of a `Client`.
+#[derive(Default, Clone)]
+pub struct TLSOption {
+    /// A `native_tls::TlsConnector` configured as desired for HTTPS connections.
+    pub connector: Option<TlsConnector>,
+}
+
+impl TLSOption {
+    /// Create a new Tls_option
+    pub fn new(connector: TlsConnector) -> Self {
+        TLSOption { connector: Some(connector) }
+    }
+
+    fn get_connector(self) -> TlsConnector {
+        self.connector.unwrap()
+    }
+}
+
+#[derive(Debug)]
+struct HttpClient {
+    client: hyper_client
+}
+
+impl HttpClient {
+    /// Constructs a new `HttpClient`.
+    fn new() -> Self {
+        HttpClient {
+            client: hyper_client::new()
+        }
+    }
+
+    /// Constructs a new `HttpClient` with option config.
+    fn new_with_option(tls_option: Option<TLSOption>) -> Self {
+        let connector = match tls_option {
+            Some(tls_connector) => {
+                let native_tls_client = NativeTlsClient::from(tls_connector.get_connector());
+                HttpsConnector::new(native_tls_client)
+            }
+            None => {
+                let ssl = NativeTlsClient::new().unwrap();
+                HttpsConnector::new(ssl)
+            }
+        };
+
+        HttpClient {
+            client: hyper_client::with_connector(connector)
+        }
+    }
+
+    /// Set the read timeout value for all requests.
+    fn set_read_timeout(&mut self, timeout: Duration) {
+        self.client.set_read_timeout(Some(timeout));
+    }
+
+    /// Set the write timeout value for all requests.
+    fn set_write_timeout(&mut self, timeout: Duration) {
+        self.client.set_write_timeout(Some(timeout));
+    }
+
+    /// Make a GET request to influxdb
+    fn get(&self, url: Url) -> RequestBuilder {
+        self.client.get(url)
+    }
+
+    /// Make a POST request to influxdb
+    fn post(&self, url: Url) -> RequestBuilder {
+        self.client.post(url)
+    }
+}
+
+impl Default for HttpClient {
+    /// Create a default `HttpClient`
+    fn default() -> Self {
+        HttpClient::new()
+    }
+}
+
+/// Udp client
+pub struct UdpClient {
+    hosts: Vec<SocketAddr>,
+}
+
+impl UdpClient {
+    /// Create a new udp client.
+    /// panic when T can't convert to SocketAddr
+    pub fn new<T: Into<String>>(address: T) -> Self {
+        UdpClient {
+            hosts: vec![address.into().to_socket_addrs().unwrap().next().unwrap()]
+        }
+    }
+
+    /// add udp host.
+    /// panic when T can't convert to SocketAddr
+    pub fn add_host<T: Into<String>>(&mut self, address: T) {
+        self.hosts.push(address.into()
+            .to_socket_addrs().unwrap().next().unwrap())
+    }
+
+    /// View current hosts
+    pub fn get_host(&self) -> Vec<SocketAddr> {
+        self.hosts.to_owned()
+    }
+
+    /// Send Points to influxdb.
+    pub fn write_points(&self, points: Points) -> Result<(), error::Error> {
+        let socket = UdpSocket::bind("0.0.0.0:0")?;
+
+        let line = serialization::line_serialization(points);
+        let line = line.as_bytes();
+        socket.send_to(&line, self.hosts.as_slice())?;
+
+        Ok(())
+    }
+
+    /// Send Point to influxdb.
+    pub fn write_point(&self, point: Point) -> Result<(), error::Error> {
+        let points = Points{ point: vec![point] };
+        self.write_points(points)
+    }
+}
+
+impl FromIterator<SocketAddr> for UdpClient {
+    /// Create udp client from iterator.
+    fn from_iter<I: IntoIterator<Item=SocketAddr>>(iter: I) -> Self {
+        let mut hosts = Vec::new();
+
+        for i in iter {
+            hosts.push(i);
+        }
+
+        UdpClient {
+            hosts
+        }
+    }
+}
diff --git a/src/error.rs b/src/error.rs
index 2486db6..d799df2 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,11 +1,21 @@
 use std::fmt;
+use std::io;
+use hyper;
 
+/// The error of influxdb client
 #[derive(Debug)]
 pub enum Error {
+    /// Syntax error, some is bug, some is SQL error. If it's a bug, welcome to PR.
     SyntaxError(String),
+    /// Invalid credentials
     InvalidCredentials(String),
+    /// The specified database does not exist
     DataBaseDoesNotExist(String),
+    /// The specified retention policy does not exist
     RetentionPolicyDoesNotExist(String),
+    /// Some error on build url or io.
+    Communication(String),
+    /// Some other error, I don't expect
     Unknow(String),
 }
 
@@ -16,7 +26,20 @@ impl fmt::Display for Error {
             Error::InvalidCredentials(ref t) => write!(f, "{}", t),
             Error::DataBaseDoesNotExist(ref t) => write!(f, "{}", t),
             Error::RetentionPolicyDoesNotExist(ref t) => write!(f, "{}", t),
+            Error::Communication(ref t) => write!(f, "{}", t),
             Error::Unknow(ref t) => write!(f, "{}", t)
         }
     }
 }
+
+impl From<io::Error> for Error {
+    fn from(err: io::Error) -> Self {
+        Error::Communication(format!("{}", err))
+    }
+}
+
+impl From<hyper::Error> for Error {
+    fn from(err: hyper::Error) -> Self {
+        Error::Communication(format!("{}", err))
+    }
+}
diff --git a/src/keys.rs b/src/keys.rs
new file mode 100644
index 0000000..9619320
--- /dev/null
+++ b/src/keys.rs
@@ -0,0 +1,200 @@
+use std::collections::HashMap;
+use std::iter::FromIterator;
+use serde_json;
+
+/// Influxdb value, Please look at [this address](https://docs.influxdata.com/influxdb/v1.3/write_protocols/line_protocol_reference/)
+#[derive(Debug, Clone)]
+pub enum Value {
+    /// string
+    String(String),
+    /// float
+    Float(f64),
+    /// Integer
+    Integer(i64),
+    /// Bool
+    Boolean(bool)
+}
+
+/// influxdb point
+#[derive(Debug, Clone)]
+pub struct Point {
+    /// measurement
+    pub measurement: String,
+    /// tags
+    pub tags: HashMap<String, Value>,
+    /// fields
+    pub fields: HashMap<String, Value>,
+    /// timestamp
+    pub timestamp: Option<i64>
+}
+
+impl Point {
+    /// Create a new point
+    pub fn new(measurement: &str) -> Point {
+        Point {
+            measurement: String::from(measurement),
+            tags: HashMap::new(),
+            fields: HashMap::new(),
+            timestamp: None,
+        }
+    }
+
+    /// Add a tag and its value
+    pub fn add_tag<T: ToString>(&mut self, tag: T, value: Value) {
+        self.tags.insert(tag.to_string(), value);
+    }
+
+    /// Add a field and its value
+    pub fn add_field<T: ToString>(&mut self, field: T, value: Value) {
+        self.fields.insert(field.to_string(), value);
+    }
+
+    /// Set the specified timestamp
+    pub fn add_timestamp(&mut self, timestamp: i64) {
+        self.timestamp = Some(timestamp);
+    }
+}
+
+/// Points
+#[derive(Debug)]
+pub struct Points {
+    /// points
+    pub point: Vec<Point>
+}
+
+impl Points {
+    /// Create a new points
+    pub fn new(point: Point) -> Points {
+        let mut points = Vec::new();
+        points.push(point);
+        Points {
+            point: points,
+        }
+    }
+
+    /// Insert point into already existing points
+    pub fn push(&mut self, point: Point) {
+        self.point.push(point)
+    }
+
+    /// Create a multi Points more directly
+    pub fn create_new(points: Vec<Point>) -> Points {
+        Points {
+            point: points
+        }
+    }
+}
+
+impl FromIterator<Point> for Points {
+    fn from_iter<T: IntoIterator<Item=Point>>(iter: T) -> Self {
+        let mut points = Vec::new();
+
+        for point in iter {
+            points.push(point);
+        }
+
+        Points {
+            point: points,
+        }
+    }
+}
+
+/// Query data
+#[derive(Debug, Clone, Deserialize, Serialize)]
+pub struct Query {
+    /// query results
+    pub results: Option<Vec<Node>>,
+    /// fail message
+    pub error: Option<String>,
+}
+
+/// Query data node
+#[derive(Debug, Clone, Deserialize, Serialize)]
+pub struct Node {
+    /// id
+    pub statement_id: Option<u64>,
+    /// series
+    pub series: Option<Vec<Series>>
+}
+
+/// Query data series
+#[derive(Debug, Clone, Deserialize, Serialize)]
+pub struct Series {
+    /// measurement
+    pub name: String,
+    /// fields and tages name and time
+    pub columns: Vec<String>,
+    /// values
+    pub values: Vec<Vec<serde_json::Value>>
+}
+
+/// Time accuracy
+#[derive(Debug)]
+pub enum Precision {
+    /// n
+    Nanoseconds,
+    /// u
+    Microseconds,
+    /// ms
+    Milliseconds,
+    /// s
+    Seconds,
+    /// m
+    Minutes,
+    /// h
+    Hours
+}
+
+impl Precision {
+    /// Convert Precision to &str
+    pub fn to_str(&self) -> &str {
+        match *self {
+            Precision::Nanoseconds => "n",
+            Precision::Microseconds => "u",
+            Precision::Milliseconds => "ms",
+            Precision::Seconds => "s",
+            Precision::Minutes => "m",
+            Precision::Hours => "h"
+        }
+    }
+}
+
+#[macro_export]
+macro_rules! points {
+    ($($x:expr),+) => {
+        {
+            let mut temp_vec = Vec::new();
+            $(temp_vec.push($x);)*
+            Points { point: temp_vec }
+        }
+    };
+}
+
+#[macro_export]
+macro_rules! point {
+    ($x:expr) => {
+        {
+            Point::new($x)
+        }
+    };
+    ($x:expr, $y:expr, $z:expr) => {
+        {
+            Point {
+                measurement: String::from($x),
+                tags: $y,
+                fields: $z,
+                timestamp: None
+            }
+        }
+    };
+    ($x:expr, $y:expr, $z:expr, $a:expr) => {
+        {
+            Point {
+                measurement: String::from($x),
+                tags: $y,
+                fields: $z,
+                timestamp: Some($a)
+            }
+        }
+    };
+}
diff --git a/src/lib.rs b/src/lib.rs
index d186fc9..7b0d376 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -4,593 +4,80 @@
 //!
 //! ## Usage
 //!
+//! ### http
+//!
 //! ```Rust
+//! #[macro_use]
 //! extern crate influx_db_client;
 //!
-//! use influx_db_client::{InfluxdbClient, Point, Points, Value, InfluxClient, Precision};
+//! use influx_db_client::{Client, Point, Points, Value, Precision};
 //!
 //! fn main() {
-//!     let mut client = InfluxdbClient::new("http://localhost:8086", "test", "root", "root");
-//!     client.set_write_timeout(10);
-//!     client.set_read_timeout(10);
+//!         // default with "http://127.0.0.1:8086", db with "test"
+//!         let client = Client::default().set_authentication("root", "root");
 //!
-//!     let mut point = Point::new("test");
-//!     point.add_field("somefield", Value::Integer(65));
+//!         let mut point = point!("test1");
+//!         point.add_field("foo", Value::String("bar".to_string()));
+//!         point.add_field("integer", Value::Integer(11));
+//!         point.add_field("float", Value::Float(22.3));
+//!         point.add_field("'boolean'", Value::Boolean(false));
 //!
-//!     let mut point1 = Point::new("test2");
-//!     point1.add_field("somefield", Value::Float(12.2));
-//!     point1.add_tag("sometag", Value::Boolean(false));
+//!         let mut point1 = point!("test2");
+//!         point1.add_tag("tags", Value::String(String::from("'=213w")));
+//!         point1.add_tag("number", Value::Integer(12));
+//!         point1.add_tag("float", Value::Float(12.6));
+//!         point1.add_field("fd", Value::String("'3'".to_string()));
 //!
-//!     let mut points = Points::new(point);
-//!     points.push(point1);
+//!         let points = points!(point1, point);
 //!
-//!     // if Precision is None, the default is second
-//!     // Multiple write
-//!     let res = client.write_points(points, Some(Precision::Microseconds), None).unwrap();
-//!     let version = client.get_version().unwrap();
-//!     println!("{}\nversion:{}", res, version)
+//!         // if Precision is None, the default is second
+//!         // Multiple write
+//!         let _ = client.write_points(points, Some(Precision::Seconds), None).unwrap();
 //!
-//!     // query
-//!     let res = client.query("select * from test", None).unwrap();
-//!     println!("{:?}", res[0].get("series").unwrap()[0].get("values"))
+//!         // query, it's type is Option<Vec<Node>>
+//!         let res = client.query("select * from test1", None).unwrap();
+//!         println!("{:?}", res.unwrap()[0].series)
 //! }
 //! ```
+//!
+//! ### udp
+//!
+//! ```Rust
+//! #[macro_use]
+//! extern crate influx_db_client;
+//!
+//! use influx_db_client::{UdpClient, Point, Value};
+//!
+//! fn main() {
+//!     let mut udp = UdpClient::new("127.0.0.1:8089");
+//!     udp.add_host("127.0.0.1:8090");
+//!
+//!     let mut point = point!("test");
+//!     point.add_field("foo", Value::String(String::from("bar")));
+//!
+//!     let _ = udp.write_point(point).unwrap();
+//! }
+//! ```
+
+#![deny(warnings)]
+#![deny(missing_docs)]
+
 extern crate hyper;
 extern crate serde_json;
+extern crate hyper_native_tls;
+extern crate serde;
+#[macro_use]
+extern crate serde_derive;
 
-use hyper::client::Client;
-use hyper::Url;
-use std::collections::HashMap;
-use std::io::Read;
-use std::time::Duration;
-
+/// Serialization module
 pub mod serialization;
+/// Error module
 pub mod error;
-
-#[derive(Debug, Clone)]
-pub enum Value {
-    String(String),
-    Float(f64),
-    Integer(i64),
-    Boolean(bool)
-}
-
-#[derive(Debug, Clone)]
-pub struct Point {
-    pub measurement: String,
-    pub tags: HashMap<String, Value>,
-    pub fields: HashMap<String, Value>,
-    pub timestamp: Option<i64>
-}
-
-#[derive(Debug)]
-pub struct Points {
-    pub point: Vec<Point>
-}
-
-#[derive(Debug)]
-pub enum Precision {
-    Nanoseconds,
-    Microseconds,
-    Milliseconds,
-    Seconds,
-    Minutes,
-    Hours
-}
-
-trait Tostr {
-    fn to_str(&self) -> &str;
-}
-
-impl Tostr for Precision {
-    /// Convert Precision to &str
-    fn to_str(&self) -> &str {
-        match *self {
-            Precision::Nanoseconds => "n",
-            Precision::Microseconds => "u",
-            Precision::Milliseconds => "ms",
-            Precision::Seconds => "s",
-            Precision::Minutes => "m",
-            Precision::Hours => "h"
-        }
-    }
-}
-
-#[derive(Debug, Clone)]
-pub struct InfluxdbClient {
-    host: String,
-    db: String,
-    username: String,
-    passwd: String,
-    read_timeout: Option<u64>,
-    write_timeout: Option<u64>,
-}
-
-pub trait InfluxClient {
-    /// Query whether the corresponding database exists, return bool
-    fn ping(&self) -> bool;
-
-    /// Query the version of the database and return the version number
-    fn get_version(&self) -> Option<String>;
-
-    /// Write a point to the database
-    fn write_point(&self, point: Point, precision: Option<Precision>, rp: Option<&str>) -> Result<bool, error::Error>;
-
-    /// Write multiple points to the database
-    ///
-    /// ```Rust
-    /// let mut client = InfluxdbClient::new("http://localhost:8086", "test", "root", "root");
-    /// client.set_write_timeout(10);
-    /// client.set_read_timeout(10);
-    ///
-    /// let mut point = Point::new("test");
-    /// point.add_field("somefield", Value::Integer(65));
-    ///
-    /// let mut point1 = Point::new("test2");
-    /// point1.add_field("somefield", Value::Float(12.2));
-    /// point1.add_tag("sometag", Value::Boolean(false));
-    ///
-    /// let mut points = Points::new(point);
-    /// points.push(point1);
-    ///
-    /// if Precision is None, the default is second
-    /// Multiple write
-    /// let res = client.write_points(points, Some(Precision::Microseconds), None).unwrap();
-    /// ```
-    fn write_points(&self, points: Points, precision: Option<Precision>, rp: Option<&str>) -> Result<bool, error::Error>;
-
-    /// Query and return data, the data type is `Vec<serde_json::Value>`, such as "show, select"
-    ///
-    /// ```Rust
-    /// let client = InfluxdbClient::new("http://localhost:8086", "test", "root", "root");
-    /// let res = client.query("select * from test", None).unwrap();
-    /// println!("{:?}", res[0].get("series").unwrap()[0].get("values"));
-    /// ```
-    fn query(&self, q: &str, epoch: Option<Precision>) -> Result<Vec<serde_json::Value>, error::Error>;
-
-    // Drop a measurement
-    fn drop_measurement(&self, measurement: &str) -> Result<(), error::Error>;
-
-    /// Create a new database in InfluxDB.
-    fn create_database(&self, dbname: &str) -> Result<(), error::Error>;
-
-    /// Drop a database from InfluxDB.
-    fn drop_database(&self, dbname: &str) -> Result<(), error::Error>;
-
-    /// Create a new user in InfluxDB.
-    fn create_user(&self, user: &str, passwd: &str, admin: bool) -> Result<(), error::Error>;
-
-    /// Drop a user from InfluxDB.
-    fn drop_user(&self, user: &str) -> Result<(), error::Error>;
-
-    /// Change the password of an existing user.
-    fn set_user_password(&self, user: &str, passwd: &str) -> Result<(), error::Error>;
-
-    /// Grant cluster administration privileges to a user.
-    fn grant_admin_privileges(&self, user: &str) -> Result<(), error::Error>;
-
-    /// Revoke cluster administration privileges from a user.
-    fn revoke_admin_privileges(&self, user: &str) -> Result<(), error::Error>;
-
-    /// Grant a privilege on a database to a user.
-    /// :param privilege: the privilege to grant, one of 'read', 'write'
-    /// or 'all'. The string is case-insensitive
-    fn grant_privilege(&self, user: &str, db: &str, privilege: &str) -> Result<(), error::Error>;
-
-    /// Revoke a privilege on a database from a user.
-    /// :param privilege: the privilege to grant, one of 'read', 'write'
-    /// or 'all'. The string is case-insensitive
-    fn revoke_privilege(&self, user: &str, db: &str, privilege: &str) -> Result<(), error::Error>;
-
-    /// Create a retention policy for a database.
-    fn create_retention_policy(&self, name: &str, duration: &str, replication: &str, default: bool, db: Option<&str>) -> Result<(), error::Error>;
-
-    /// Drop an existing retention policy for a database.
-    fn drop_retention_policy(&self, name: &str, db: Option<&str>) -> Result<(), error::Error>;
-}
-
-trait Query {
-    /// Query and return to the native json structure
-    fn query_raw(&self, q: &str, epoch: Option<Precision>) -> Result<serde_json::Value, error::Error>;
-}
-
-impl InfluxdbClient {
-    /// Create a new influxdb client
-    pub fn new<T>(host: T, db: T, username: T, passwd: T) -> Self
-        where T: ToString {
-        InfluxdbClient {
-            host: host.to_string(),
-            db: db.to_string(),
-            username: username.to_string(),
-            passwd: passwd.to_string(),
-            read_timeout: None,
-            write_timeout: None,
-        }
-    }
-
-    /// Set the read timeout value
-    pub fn set_read_timeout(&mut self, timeout: u64) {
-        self.read_timeout = Some(timeout);
-    }
-
-    /// Set the write timeout value
-    pub fn set_write_timeout(&mut self, timeout: u64) {
-        self.write_timeout = Some(timeout);
-    }
-
-    /// Change the client's database
-    pub fn swith_database<T>(&mut self, database: T) where T: ToString {
-        self.db = database.to_string();
-    }
-
-    /// Change the client's user
-    pub fn swith_user<T>(&mut self, user: T, passwd: T) where T: ToString {
-        self.username = user.to_string();
-        self.passwd = passwd.to_string();
-    }
-}
-
-unsafe impl Send for InfluxdbClient {}
-
-impl Query for InfluxdbClient {
-    /// Query and return to the native json structure
-    fn query_raw(&self, q: &str, epoch: Option<Precision>) -> Result<serde_json::Value, error::Error> {
-        let mut param = vec![("db", self.db.as_str()), ("u", self.username.as_str()), ("p", self.passwd.as_str()), ("q", q)];
-
-        match epoch {
-            Some(ref t) => param.push(("epoch", t.to_str())),
-            None => ()
-        }
-
-        let mut client = Client::new();
-
-        match self.read_timeout {
-            Some(t) => client.set_read_timeout(Some(Duration::new(t, 0))),
-            None => ()
-        }
-
-        match self.write_timeout {
-            Some(t) => client.set_write_timeout(Some(Duration::new(t, 0))),
-            None => ()
-        }
-
-        let url = Url::parse(&self.host).unwrap();
-        let url = url.join("query").unwrap();
-        let url = Url::parse_with_params(url.as_str(), &param).unwrap();
-
-        let q_lower = q.to_lowercase();
-        let mut res = {
-            if q_lower.starts_with("select") && !q_lower.contains("into") || q_lower.starts_with("show") {
-                client.get(url).send().unwrap()
-            } else {
-                client.post(url).send().unwrap()
-            }
-        };
-
-        let mut context = String::new();
-        let _ = res.read_to_string(&mut context);
-
-        let json_data = serde_json::from_str(&context).unwrap();
-
-        match res.status_raw().0 {
-            200 => Ok(json_data),
-            400 => Err(error::Error::SyntaxError(serialization::conversion(json_data.get("error").unwrap().to_string()))),
-            401 => Err(error::Error::InvalidCredentials("Invalid authentication credentials.".to_string())),
-            _ => Err(error::Error::Unknow("There is something wrong".to_string()))
-        }
-    }
-}
-
-impl InfluxClient for InfluxdbClient {
-    /// Query whether the corresponding database exists, return bool
-    fn ping(&self) -> bool {
-        let ping = Client::new();
-        let url = Url::parse(&self.host).unwrap();
-        let url = url.join("ping").unwrap();
-        let res = ping.get(url).send().unwrap();
-        match res.status_raw().0 {
-            204 => true,
-            _ => false,
-        }
-    }
-
-    /// Query the version of the database and return the version number
-    fn get_version(&self) -> Option<String> {
-        let ping = Client::new();
-        let url = Url::parse(&self.host).unwrap();
-        let url = url.join("ping").unwrap();
-        let res = ping.get(url).send().unwrap();
-        match res.status_raw().0 {
-            204 => match res.headers.get_raw("X-Influxdb-Version") {
-                Some(i) => Some(String::from_utf8(i[0].to_vec()).unwrap()),
-                None => Some(String::from("Don't know"))
-            },
-            _ => None,
-        }
-    }
-
-    /// Write a point to the database
-    fn write_point(&self, point: Point, precision: Option<Precision>, rp: Option<&str>) -> Result<bool, error::Error> {
-        let points = Points::new(point);
-        self.write_points(points, precision, rp)
-    }
-
-    /// Write multiple points to the database
-    fn write_points(&self, points: Points, precision: Option<Precision>, rp: Option<&str>) -> Result<bool, error::Error> {
-        let line = serialization::line_serialization(points);
-
-        let mut param = vec![("db", self.db.as_str()), ("u", self.username.as_str()), ("p", self.passwd.as_str())];
-
-        match precision {
-            Some(ref t) => param.push(("precision", t.to_str())),
-            None => param.push(("precision", "s")),
-        };
-
-        match rp {
-            Some(t) => param.push(("rp", t)),
-            None => (),
-        };
-
-        let mut client = Client::new();
-
-        match self.read_timeout {
-            Some(t) => client.set_read_timeout(Some(Duration::new(t, 0))),
-            None => ()
-        }
-
-        match self.write_timeout {
-            Some(t) => client.set_write_timeout(Some(Duration::new(t, 0))),
-            None => ()
-        }
-
-        let url = Url::parse(&self.host).unwrap();
-        let url = url.join("write").unwrap();
-        let url = Url::parse_with_params(url.as_str(), &param).unwrap();
-
-        let mut res = client.post(url).body(&line).send().unwrap();
-        let mut err = String::new();
-        let _ = res.read_to_string(&mut err);
-
-        match res.status_raw().0 {
-            204 => Ok(true),
-            400 => Err(error::Error::SyntaxError(serialization::conversion(err))),
-            401 => Err(error::Error::InvalidCredentials("Invalid authentication credentials.".to_string())),
-            404 => Err(error::Error::DataBaseDoesNotExist(serialization::conversion(err))),
-            500 => Err(error::Error::RetentionPolicyDoesNotExist(err)),
-            _ => Err(error::Error::Unknow("There is something wrong".to_string()))
-        }
-    }
-
-    /// Query and return data, the data type is `Vec<serde_json::Value>`
-    fn query(&self, q: &str, epoch: Option<Precision>) -> Result<Vec<serde_json::Value>, error::Error> {
-        match self.query_raw(q, epoch) {
-            Ok(t) => Ok(t.get("results").unwrap().as_array().unwrap().to_vec()),
-            Err(e) => Err(e)
-        }
-    }
-
-    fn drop_measurement(&self, measurement: &str) -> Result<(), error::Error> {
-        let sql = format!("Drop measurement {}", serialization::quote_ident(measurement));
-
-        match self.query_raw(&sql, None) {
-            Ok(_) => Ok(()),
-            Err(e) => Err(e)
-        }
-    }
-
-    /// Create a new database in InfluxDB.
-    fn create_database(&self, dbname: &str) -> Result<(), error::Error> {
-        let sql = format!("Create database {}", serialization::quote_ident(dbname));
-
-        match self.query_raw(&sql, None) {
-            Ok(_) => Ok(()),
-            Err(e) => Err(e)
-        }
-    }
-
-    /// Drop a database from InfluxDB.
-    fn drop_database(&self, dbname: &str) -> Result<(), error::Error> {
-        let sql = format!("Drop database {}", serialization::quote_ident(dbname));
-
-        match self.query_raw(&sql, None) {
-            Ok(_) => Ok(()),
-            Err(e) => Err(e)
-        }
-    }
-
-    /// Create a new user in InfluxDB.
-    fn create_user(&self, user: &str, passwd: &str, admin: bool) -> Result<(), error::Error> {
-        let sql: String = {
-            if admin {
-                format!("Create user {0} with password {1} with all privileges",
-                        serialization::quote_ident(user), serialization::quote_literal(passwd))
-            } else {
-                format!("Create user {0} WITH password {1}", serialization::quote_ident(user),
-                        serialization::quote_literal(passwd))
-            }
-        };
-
-        match self.query_raw(&sql, None) {
-            Ok(_) => Ok(()),
-            Err(e) => Err(e)
-        }
-    }
-
-    /// Drop a user from InfluxDB.
-    fn drop_user(&self, user: &str) -> Result<(), error::Error> {
-        let sql = format!("Drop user {}", serialization::quote_ident(user));
-
-        match self.query_raw(&sql, None) {
-            Ok(_) => Ok(()),
-            Err(e) => Err(e)
-        }
-    }
-
-    /// Change the password of an existing user.
-    fn set_user_password(&self, user: &str, passwd: &str) -> Result<(), error::Error> {
-        let sql = format!("Set password for {}={}", serialization::quote_ident(user),
-                          serialization::quote_literal(passwd));
-
-        match self.query_raw(&sql, None) {
-            Ok(_) => Ok(()),
-            Err(e) => Err(e)
-        }
-    }
-
-    /// Grant cluster administration privileges to a user.
-    fn grant_admin_privileges(&self, user: &str) -> Result<(), error::Error> {
-        let sql = format!("Grant all privileges to {}", serialization::quote_ident(user));
-
-        match self.query_raw(&sql, None) {
-            Ok(_) => Ok(()),
-            Err(e) => Err(e)
-        }
-    }
-
-    /// Revoke cluster administration privileges from a user.
-    fn revoke_admin_privileges(&self, user: &str) -> Result<(), error::Error> {
-        let sql = format!("Revoke all privileges from {}", serialization::quote_ident(user));
-
-        match self.query_raw(&sql, None) {
-            Ok(_) => Ok(()),
-            Err(e) => Err(e)
-        }
-    }
-
-    /// Grant a privilege on a database to a user.
-    /// :param privilege: the privilege to grant, one of 'read', 'write'
-    /// or 'all'. The string is case-insensitive
-    fn grant_privilege(&self, user: &str, db: &str, privilege: &str) -> Result<(), error::Error> {
-        let sql = format!("Grant {} on {} to {}", privilege, serialization::quote_ident(db),
-                          serialization::quote_ident(user));
-
-        match self.query_raw(&sql, None) {
-            Ok(_) => Ok(()),
-            Err(e) => Err(e)
-        }
-    }
-
-    /// Revoke a privilege on a database from a user.
-    /// :param privilege: the privilege to grant, one of 'read', 'write'
-    /// or 'all'. The string is case-insensitive
-    fn revoke_privilege(&self, user: &str, db: &str, privilege: &str) -> Result<(), error::Error> {
-        let sql = format!("Revoke {0} on {1} from {2}", privilege, serialization::quote_ident(db),
-                          serialization::quote_ident(user));
-
-        match self.query_raw(&sql, None) {
-            Ok(_) => Ok(()),
-            Err(e) => Err(e)
-        }
-    }
-
-    /// Create a retention policy for a database.
-    /// :param duration: the duration of the new retention policy.
-    ///  Durations such as 1h, 90m, 12h, 7d, and 4w, are all supported
-    ///  and mean 1 hour, 90 minutes, 12 hours, 7 day, and 4 weeks,
-    ///  respectively. For infinite retention – meaning the data will
-    ///  never be deleted – use 'INF' for duration.
-    ///  The minimum retention period is 1 hour.
-    fn create_retention_policy(&self, name: &str, duration: &str, replication: &str, default: bool, db: Option<&str>) -> Result<(), error::Error> {
-        let database = {
-            if let Some(t) = db {
-                t
-            } else {
-                &self.db
-            }
-        };
-
-        let sql: String = {
-            if default {
-                format!("Create retention policy {} on {} duration {} replication {} default",
-                        serialization::quote_ident(name), serialization::quote_ident(database), duration, replication)
-            } else {
-                format!("Create retention policy {} on {} duration {} replication {}",
-                        serialization::quote_ident(name), serialization::quote_ident(database), duration, replication)
-            }
-        };
-
-        match self.query_raw(&sql, None) {
-            Ok(_) => Ok(()),
-            Err(e) => Err(e)
-        }
-    }
-
-    /// Drop an existing retention policy for a database.
-    fn drop_retention_policy(&self, name: &str, db: Option<&str>) -> Result<(), error::Error> {
-        let database = {
-            if let Some(t) = db {
-                t
-            } else {
-                &self.db
-            }
-        };
-
-        let sql = format!("Drop retention policy {} on {}", serialization::quote_ident(name),
-                          serialization::quote_ident(database));
-
-        match self.query_raw(&sql, None) {
-            Ok(_) => Ok(()),
-            Err(e) => Err(e)
-        }
-    }
-}
-
-impl Point {
-    /// Create a new point
-    pub fn new(measurement: &str) -> Point {
-        Point {
-            measurement: String::from(measurement),
-            tags: HashMap::new(),
-            fields: HashMap::new(),
-            timestamp: None,
-        }
-    }
-
-    /// Add a tag and its value
-    pub fn add_tag<T: ToString>(&mut self, tag: T, value: Value) {
-        self.tags.insert(tag.to_string(), value);
-    }
-
-    /// Add a field and its value
-    pub fn add_field<T: ToString>(&mut self, field: T, value: Value) {
-        self.fields.insert(field.to_string(), value);
-    }
-
-    /// Set the specified timestamp
-    pub fn add_timestamp(&mut self, timestamp: i64) {
-        self.timestamp = Some(timestamp);
-    }
-
-    /// Create a complete point
-    pub fn create_new(measuremnet: &str, tags: HashMap<String, Value>, fields: HashMap<String, Value>, timestamp: i64) -> Self {
-        Point {
-            measurement: String::from(measuremnet),
-            tags: tags,
-            fields: fields,
-            timestamp: Some(timestamp),
-        }
-    }
-}
-
-impl Points {
-    /// Create a new points
-    pub fn new(point: Point) -> Points {
-        let mut points = Vec::new();
-        points.push(point);
-        Points {
-            point: points,
-        }
-    }
-
-    /// Insert point into already existing points
-    pub fn push(&mut self, point: Point) {
-        self.point.push(point)
-    }
-
-    /// Create a multi Points more directly
-    pub fn create_new(points: Vec<Point>) -> Points {
-        Points {
-            point: points
-        }
-    }
-}
+/// All API on influxdb client, Including udp, http
+pub mod client;
+/// Points and Query Data Deserialize
+pub mod keys;
+
+pub use client::{ Client, UdpClient, TLSOption };
+pub use keys::{ Point, Precision, Points, Value, Node, Query, Series };
+pub use error::Error;
diff --git a/src/serialization.rs b/src/serialization.rs
index 8d3b91f..5e84696 100644
--- a/src/serialization.rs
+++ b/src/serialization.rs
@@ -1,27 +1,27 @@
 use ::{Points, Value};
 
 /// Resolve the points to line protocol format
-pub fn line_serialization(points: Points) -> String {
+pub(crate) fn line_serialization(points: Points) -> String {
     let mut line = Vec::new();
     for point in points.point {
         line.push(escape_measurement(point.measurement));
 
-        for (tag, value) in point.tags.iter() {
+        for (tag, value) in point.tags.into_iter() {
             line.push(",".to_string());
             line.push(escape_keys_and_tags(tag.to_string()));
             line.push("=".to_string());
 
             match value {
-                &Value::String(ref s) => line.push(escape_keys_and_tags(s.to_string())),
-                &Value::Float(ref f) => line.push(f.to_string()),
-                &Value::Integer(ref i) => line.push(i.to_string() + "i"),
-                &Value::Boolean(b) => line.push({ if b { "true".to_string() } else { "false".to_string() } })
+                Value::String(s) => line.push(escape_keys_and_tags(s.to_string())),
+                Value::Float(f) => line.push(f.to_string()),
+                Value::Integer(i) => line.push(i.to_string() + "i"),
+                Value::Boolean(b) => line.push({ if b { "true".to_string() } else { "false".to_string() } })
             }
         }
 
         let mut was_first = true;
 
-        for (field, value) in point.fields.iter() {
+        for (field, value) in point.fields.into_iter() {
             line.push({
                 if was_first {
                     was_first = false;
@@ -32,10 +32,10 @@ pub fn line_serialization(points: Points) -> String {
             line.push("=".to_string());
 
             match value {
-                &Value::String(ref s) => line.push(escape_string_field_value(s.to_string())),
-                &Value::Float(ref f) => line.push(f.to_string()),
-                &Value::Integer(ref i) => line.push(i.to_string() + "i"),
-                &Value::Boolean(b) => line.push({ if b { "true".to_string() } else { "false".to_string() } })
+                Value::String(s) => line.push(escape_string_field_value( s.to_string())),
+                Value::Float(f) => line.push(f.to_string()),
+                Value::Integer(i) => line.push(i.to_string() + "i"),
+                Value::Boolean(b) => line.push({ if b { "true".to_string() } else { "false".to_string() } })
             }
         }
 
@@ -54,17 +54,17 @@ pub fn line_serialization(points: Points) -> String {
 }
 
 #[inline]
-pub fn quote_ident(value: &str) -> String {
+pub(crate) fn quote_ident(value: &str) -> String {
     format!("\"{}\"", value.replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", "\\n"))
 }
 
 #[inline]
-pub fn quote_literal(value: &str) -> String {
+pub(crate) fn quote_literal(value: &str) -> String {
     format!("'{}'", value.replace("\\", "\\\\").replace("'", "\\'"))
 }
 
 #[inline]
-pub fn conversion(value: String) -> String {
+pub(crate) fn conversion(value: String) -> String {
     value.replace("\'", "").replace("\"", "").replace("\\", "").trim().to_string()
 }
 
@@ -80,7 +80,7 @@ fn escape_measurement(value: String) -> String {
 
 #[inline]
 fn escape_string_field_value(value: String) -> String {
-    value.replace("\"", "\\\"")
+    format!("\"{}\"", value.replace("\"", "\\\""))
 }
 
 #[cfg(test)]
@@ -95,7 +95,7 @@ mod test {
         point.add_tag("sometag", Value::Boolean(false));
         let points = Points::new(point);
 
-        assert_eq!(line_serialization(points), "test,sometag=false somefield=65\n")
+        assert_eq!(line_serialization(points), "test,sometag=false somefield=65i\n")
     }
 
     #[test]
@@ -110,7 +110,7 @@ mod test {
 
     #[test]
     fn escape_string_field_value_test() {
-        assert_eq!(escape_string_field_value(String::from("\"foo")) , "\\\"foo")
+        assert_eq!(escape_string_field_value(String::from("\"foo")) , "\"\\\"foo\"")
     }
 
     #[test]


Test patch:
diff --git a/tests/client_test.rs b/tests/client_test.rs
new file mode 100644
index 0000000..a94f5f9
--- /dev/null
+++ b/tests/client_test.rs
@@ -0,0 +1,135 @@
+#[macro_use]
+extern crate influx_db_client;
+extern crate native_tls;
+
+use influx_db_client::{ Client, UdpClient, Point, Points, Precision, Value, TLSOption };
+use native_tls::{ TlsConnector, Certificate };
+use std::thread::sleep;
+use std::time::Duration;
+use std::collections::HashMap;
+use std::io::Read;
+use std::fs::File;
+
+#[test]
+fn create_and_delete_database() {
+    let client = Client::default().set_authentication("root", "root");
+
+    let _ = client.create_database("temporary").unwrap();
+
+    let _ = client.drop_database("temporary").unwrap();
+}
+
+#[test]
+fn create_and_delete_measurement() {
+    let client = Client::default().set_authentication("root", "root");
+    let mut point = Point::new("temporary");
+    point.add_field("foo", Value::String("bar".to_string()));
+    point.add_field("integer", Value::Integer(11));
+    point.add_field("float", Value::Float(22.3));
+    point.add_field("'boolean'", Value::Boolean(false));
+
+    let _ = client.write_point(point,Some(Precision::Seconds), None).unwrap();
+
+    let _ = client.drop_measurement("temporary").unwrap();
+}
+
+#[test]
+fn use_points() {
+    let client = Client::default().set_authentication("root", "root");
+    let mut point = Point::new("test1");
+    point.add_field("foo", Value::String("bar".to_string()));
+    point.add_field("integer", Value::Integer(11));
+    point.add_field("float", Value::Float(22.3));
+    point.add_field("'boolean'", Value::Boolean(false));
+
+    let mut point1 = Point::new("test2");
+    point1.add_tag("tags", Value::String(String::from("'=213w")));
+    point1.add_tag("number", Value::Integer(12));
+    point1.add_tag("float", Value::Float(12.6));
+    point1.add_field("fd", Value::String("'3'".to_string()));
+
+    let points = Points::create_new(vec![point1, point]);
+
+    let _ = client.write_points(points, Some(Precision::Seconds), None).unwrap();
+
+    let _ = sleep(Duration::from_secs(3));
+
+    let _ = client.drop_measurement("test1").unwrap();
+    let _ = client.drop_measurement("test2").unwrap();
+}
+
+#[test]
+fn query() {
+    let client = Client::default().set_authentication("root", "root");
+    let mut point = Point::new("test3");
+    point.add_field("foo", Value::String("bar".to_string()));
+    let mut point1 = point.clone();
+    point.add_timestamp(1508981970);
+    point1.add_timestamp(1508982026);
+
+    let _ = client.write_point(point, None, None);
+
+    let _ = client.query("select * from test3", None).unwrap();
+
+    let _ = client.write_point(point1, None, None).unwrap();
+
+    let _ = client.drop_measurement("test3").unwrap();
+}
+
+#[test]
+fn use_macro() {
+    let client = Client::default().set_authentication("root", "root");
+    let mut point = point!("test4");
+    point.add_field("foo", Value::String("bar".to_string()));
+    let mut point1 = point.clone();
+    point.add_timestamp(1508981970);
+    point1.add_timestamp(1508982026);
+
+    let points = points![point, point1];
+    let _ = client.write_points(points, None, None);
+
+    let _ = client.query("select * from test4", None).unwrap();
+
+    let _ = client.drop_measurement("test4").unwrap();
+}
+
+#[test]
+fn use_udp() {
+    let mut udp = UdpClient::new("127.0.0.1:8089");
+    udp.add_host("127.0.0.1:8090");
+    let mut client = Client::default().set_authentication("root", "root");
+
+    let mut point = point!("test");
+    point.add_field("foo", Value::String(String::from("bar")));
+
+    let _ = udp.write_point(point).unwrap();
+
+    let _ = sleep(Duration::from_secs(1));
+    client.swith_database("udp");
+    let _ = client.drop_measurement("test").unwrap();
+    client.swith_database("telegraf");
+    let _ = client.drop_measurement("test").unwrap();
+}
+
+#[test]
+fn use_https() {
+    let mut ca_cert_file = File::open("/etc/ssl/influxdb-selfsigned.crt").unwrap();
+    let mut ca_cert_buffer = Vec::new();
+    ca_cert_file.read_to_end(&mut ca_cert_buffer).unwrap();
+
+    let mut builder = TlsConnector::builder().unwrap();
+    builder.add_root_certificate(Certificate::from_der(&ca_cert_buffer).unwrap()).unwrap();
+
+    let tls_connector = TLSOption::new(builder.build().unwrap());
+
+    let client = Client::new_with_option("https://127.0.0.1:8086", "test", Some(tls_connector));
+
+    let mut point = point!("foo");
+    point.add_field("foo", Value::String(String::from("bar")));
+
+    let _ = client.write_point(point, None, None).unwrap();
+
+    let _ = client.query("select * from foo", None).unwrap();
+
+    let _ = client.drop_measurement("foo").unwrap();
+}