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

Issue:
new panics
```rust
pub fn new<T: Into<String>>(address: T) -> Self {
        UdpClient {
            hosts: vec![address.into().to_socket_addrs().unwrap().next().unwrap()],
        }
    }
```
If the hostname isn't resolvable then the udp client panics on unwrap(). 
It would be great if new returned a Result instead (or alternatively had a try_new fn).

## Workaround:

match against address.into().to_socket_addrs() and check that doesn't error first before calling new.


Discussion / hints:
https://github.com/driftluo/InfluxDBClient-rs/blob/24740b3161dfe9cfc1dd6cc78b0873ac8eb3e741/src/client.rs#L595-L600

Docs already explained, if it can't be converted, it will panic, maybe not clear enough here?
Yeah, I just didn't want to catch a panic in my code.
> Yeah, I just didn't want to catch a panic in my code.

I can only say that your thoughts are too naive. In the standard library, panic is normal when the memory cannot be allocated.

ref: https://doc.rust-lang.org/src/alloc/raw_vec.rs.html#83

### Here is my personal opinion of panic:
If a key structure initialization cannot be done, there are two ways to deal with it:
1. return Result(***will warning on Clippy check***)/Option, etc. 
2. panic

These are all means to prevent further spread of errors. 

If the document does not have the possibility of writing panic, it is panic directly. That is the problem of the library. 

If it is written, the caller does not pay attention. I think this is not a problem in the library.

If this is a binary repo, panic behavior is not allowed regardless of what the user enters.

There are still many assumptions in this library, for example, suppose the caller knows that this library uses the standard IP protocol and cannot pass unresolved URI information in the host. This assumption also exists in `httpclient`.

Let me think about it, emmm, I think users should be able to understand the appeal question. However, it may indeed require some repairs to make this library more robust.

In my opinion, in libraries stuff that can go wrong during run-time which are not necessarily programmer errors or unrecoverable like allocation errors, shouldn't cause panics.
In an application the address to connect to may come from end-user input like command line flags or environment variables, so it should be easy for a user of the library to handle the error properly to print a nice error message for the end-user.
So for this specific case I guess returning a `Result` would indeed be nicer :slightly_smiling_face: 
I suggest changing the function to receive SocketAddr directly.

Fix patch:
diff --git a/src/client.rs b/src/client.rs
index 42ad88f..8f66482 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -2,10 +2,7 @@ use bytes::Bytes;
 use futures::prelude::*;
 use reqwest::{Client as HttpClient, Response, Url};
 use serde_json::de::IoRead;
-use std::io::Cursor;
-use std::iter::FromIterator;
-use std::net::UdpSocket;
-use std::net::{SocketAddr, ToSocketAddrs};
+use std::{io::Cursor, iter::FromIterator, net::SocketAddr, net::UdpSocket};
 
 use crate::{error, serialization, ChunkedQuery, Node, Point, Points, Precision, Query};
 
@@ -63,8 +60,8 @@ impl Client {
     }
 
     /// View the current db name
-    pub fn get_db(&self) -> String {
-        self.db.to_owned()
+    pub fn get_db(&self) -> &str {
+        self.db.as_str()
     }
 
     /// Query whether the corresponding database exists, return bool
@@ -466,8 +463,8 @@ impl Client {
 
         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()
+        if let Some(param) = param {
+            Url::parse_with_params(url.as_str(), param).unwrap()
         } else {
             url
         }
@@ -488,23 +485,20 @@ pub struct UdpClient {
 
 impl UdpClient {
     /// Create a new udp client.
-    /// panic when T can't convert to SocketAddr
-    pub fn new<T: Into<String>>(address: T) -> Self {
+    pub fn new(address: SocketAddr) -> Self {
         UdpClient {
-            hosts: vec![address.into().to_socket_addrs().unwrap().next().unwrap()],
+            hosts: vec![address],
         }
     }
 
     /// 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())
+    pub fn add_host(&mut self, address: SocketAddr) {
+        self.hosts.push(address)
     }
 
     /// View current hosts
-    pub fn get_host(&self) -> Vec<SocketAddr> {
-        self.hosts.to_owned()
+    pub fn get_host(&self) -> &[SocketAddr] {
+        self.hosts.as_ref()
     }
 
     /// Send Points to influxdb.
diff --git a/src/keys.rs b/src/keys.rs
index cb9fb3a..4c2bf4e 100644
--- a/src/keys.rs
+++ b/src/keys.rs
@@ -42,19 +42,19 @@ impl Point {
     }
 
     /// Add a tag and its value
-    pub fn add_tag<T: Into<String>>(&mut self, tag: T, value: Value) -> &mut Self {
+    pub fn add_tag<T: Into<String>>(mut self, tag: T, value: Value) -> Self {
         self.tags.insert(tag.into(), value);
         self
     }
 
     /// Add a field and its value
-    pub fn add_field<T: Into<String>>(&mut self, field: T, value: Value) -> &mut Self {
+    pub fn add_field<T: Into<String>>(mut self, field: T, value: Value) -> Self {
         self.fields.insert(field.into(), value);
         self
     }
 
     /// Set the specified timestamp
-    pub fn add_timestamp(&mut self, timestamp: i64) -> &mut Self {
+    pub fn add_timestamp(mut self, timestamp: i64) -> Self {
         self.timestamp = Some(timestamp);
         self
     }
@@ -76,7 +76,7 @@ impl Points {
     }
 
     /// Insert point into already existing points
-    pub fn push(&mut self, point: Point) -> &mut Self {
+    pub fn push(mut self, point: Point) -> Self {
         self.point.push(point);
         self
     }
@@ -134,11 +134,11 @@ pub struct Series {
     /// measurement
     pub name: String,
     /// tag
-    pub tags: Option<serde_json::Map<String, serde_json::Value>>,
+    pub tags: Option<HashMap<String, Value>>,
     /// field names and time
     pub columns: Vec<String>,
     /// values
-    pub values: Vec<Vec<serde_json::Value>>,
+    pub values: Vec<Vec<Value>>,
 }
 
 /// Time accuracy
diff --git a/src/lib.rs b/src/lib.rs
index a009061..773cf69 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -7,60 +7,49 @@
 //! ### http
 //!
 //! ```Rust
-//! #[macro_use]
-//! extern crate influx_db_client;
-//!
 //! use influx_db_client::{Client, Point, Points, Value, Precision};
 //!
-//! fn main() {
-//!         // default with "http://127.0.0.1:8086", db with "test"
-//!         let client = Client::default().set_authentication("root", "root");
-//!
-//!         let mut point = point!("test1");
-//!         point
-//!             .add_field("foo", Value::String("bar".to_string()))
-//!             .add_field("integer", Value::Integer(11))
-//!             .add_field("float", Value::Float(22.3))
-//!             .add_field("'boolean'", Value::Boolean(false));
-//!
-//!         let point1 = Point::new("test1")
-//!             .add_tag("tags", Value::String(String::from("\\\"fda")))
-//!             .add_tag("number", Value::Integer(12))
-//!             .add_tag("float", Value::Float(12.6))
-//!             .add_field("fd", Value::String("'3'".to_string()))
-//!             .add_field("quto", Value::String("\\\"fda".to_string()))
-//!             .add_field("quto1", Value::String("\"fda".to_string()))
-//!             .to_owned();
-//!
-//!         let points = points!(point1, point);
-//!
-//!         // if Precision is None, the default is second
-//!         // Multiple write
-//!         let _ = client.write_points(points, Some(Precision::Seconds), None).unwrap();
-//!
-//!         // query, it's type is Option<Vec<Node>>
-//!         let res = client.query("select * from test1", None).unwrap();
-//!         println!("{:?}", res.unwrap()[0].series)
-//! }
+//! // default with "http://127.0.0.1:8086", db with "test"
+//! let client = Client::default().set_authentication("root", "root");
+//!
+//! let mut point = point!("test1");
+//! let point = point
+//!       .add_field("foo", Value::String("bar".to_string()))
+//!       .add_field("integer", Value::Integer(11))
+//!       .add_field("float", Value::Float(22.3))
+//!       .add_field("'boolean'", Value::Boolean(false));
+//!
+//! let point1 = Point::new("test1")
+//!       .add_tag("tags", Value::String(String::from("\\\"fda")))
+//!       .add_tag("number", Value::Integer(12))
+//!       .add_tag("float", Value::Float(12.6))
+//!       .add_field("fd", Value::String("'3'".to_string()))
+//!       .add_field("quto", Value::String("\\\"fda".to_string()))
+//!       .add_field("quto1", Value::String("\"fda".to_string()));
+//!
+//! let points = points!(point1, point);
+//!
+//! // if Precision is None, the default is second
+//! // Multiple write
+//! client.write_points(points, Some(Precision::Seconds), None).unwrap();
+//!
+//! // 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 udp = UdpClient::new("127.0.0.1:8089".parse().unwrap());
+//! udp.add_host("127.0.0.1:8090".parse().unwrap());
 //!
-//!     let mut point = point!("test");
-//!     point.add_field("foo", Value::String(String::from("bar")));
+//! let mut point = point!("test");
+//! point.add_field("foo", Value::String(String::from("bar")));
 //!
-//!     let _ = udp.write_point(point).unwrap();
-//! }
+//! udp.write_point(point).unwrap();
 //! ```
 
 #![deny(warnings)]
diff --git a/src/serialization.rs b/src/serialization.rs
index 612ec48..740eee0 100644
--- a/src/serialization.rs
+++ b/src/serialization.rs
@@ -120,9 +120,9 @@ mod test {
 
     #[test]
     fn line_serialization_test() {
-        let mut point = Point::new("test");
-        point.add_field("somefield", Value::Integer(65));
-        point.add_tag("sometag", Value::Boolean(false));
+        let point = Point::new("test")
+            .add_field("somefield", Value::Integer(65))
+            .add_tag("sometag", Value::Boolean(false));
         let points = Points::new(point);
 
         assert_eq!(


Test patch:
diff --git a/tests/client_test.rs b/tests/client_test.rs
index 2805b21..980e5e2 100644
--- a/tests/client_test.rs
+++ b/tests/client_test.rs
@@ -1,7 +1,4 @@
-#[macro_use]
-extern crate influx_db_client;
-
-use influx_db_client::{Client, Point, Points, Precision, UdpClient, Value};
+use influx_db_client::{point, points, Client, Point, Points, Precision, UdpClient, Value};
 use std::fs::File;
 use std::io::Read;
 use std::thread::sleep;
@@ -16,9 +13,9 @@ fn create_and_delete_database() {
     block_on(async {
         let client = Client::default().set_authentication("root", "root");
 
-        let _ = client.create_database("temporary").await.unwrap();
+        client.create_database("temporary").await.unwrap();
 
-        let _ = client.drop_database("temporary").await.unwrap();
+        client.drop_database("temporary").await.unwrap();
     });
 }
 
@@ -27,21 +24,20 @@ fn create_and_delete_measurement() {
     block_on(async {
         let mut client = Client::default().set_authentication("root", "root");
         client.switch_database("test_create_and_delete_measurement");
-        let _ = client.create_database(client.get_db().as_str()).await.unwrap();
+        client.create_database(client.get_db()).await.unwrap();
         let point = Point::new("temporary")
             .add_field("foo", Value::String("bar".to_string()))
             .add_field("integer", Value::Integer(11))
             .add_field("float", Value::Float(22.3))
-            .add_field("'boolean'", Value::Boolean(false))
-            .to_owned();
+            .add_field("'boolean'", Value::Boolean(false));
 
-        let _ = client
+        client
             .write_point(point, Some(Precision::Seconds), None)
             .await
             .unwrap();
 
-        let _ = client.drop_measurement("temporary").await.unwrap();
-        let _ = client.drop_database(client.get_db().as_str()).await.unwrap();
+        client.drop_measurement("temporary").await.unwrap();
+        client.drop_database(client.get_db()).await.unwrap();
     });
 }
 
@@ -50,33 +46,31 @@ fn use_points() {
     block_on(async {
         let mut client = Client::default().set_authentication("root", "root");
         client.switch_database("test_use_points");
-        let _ = client.create_database(client.get_db().as_str()).await.unwrap();
+        client.create_database(client.get_db()).await.unwrap();
         let point = Point::new("test1")
             .add_field("foo", Value::String("bar".to_string()))
             .add_field("integer", Value::Integer(11))
             .add_field("float", Value::Float(22.3))
-            .add_field("'boolean'", Value::Boolean(false))
-            .to_owned();
+            .add_field("'boolean'", Value::Boolean(false));
 
         let point1 = Point::new("test2")
             .add_tag("tags", Value::String(String::from("'=213w")))
             .add_tag("number", Value::Integer(12))
             .add_tag("float", Value::Float(12.6))
-            .add_field("fd", Value::String("'3'".to_string()))
-            .to_owned();
+            .add_field("fd", Value::String("'3'".to_string()));
 
         let points = Points::create_new(vec![point1, point]);
 
-        let _ = client
+        client
             .write_points(points, Some(Precision::Seconds), None)
             .await
             .unwrap();
 
-        let _ = sleep(Duration::from_secs(3));
+        sleep(Duration::from_secs(3));
 
-        let _ = client.drop_measurement("test1").await.unwrap();
-        let _ = client.drop_measurement("test2").await.unwrap();
-        let _ = client.drop_database(client.get_db().as_str()).await;
+        client.drop_measurement("test1").await.unwrap();
+        client.drop_measurement("test2").await.unwrap();
+        client.drop_database(client.get_db()).await.unwrap();
     });
 }
 
@@ -86,58 +80,56 @@ fn query() {
         let dbname = "test_query";
         let mut client = Client::default().set_authentication("root", "root");
         client.switch_database(dbname);
-        let _ = client.create_database(client.get_db().as_str()).await.unwrap();
-        let mut point = Point::new("test3")
-            .add_field("foo", Value::String("bar".to_string()))
-            .to_owned();
-        let mut point1 = point.clone();
-        point.add_timestamp(1508981970);
-        point1.add_timestamp(1508982026);
-
-        let _ = client.write_point(point, None, None).await.unwrap();
-        let _ = client.query("select * from test3", None).await.unwrap();
-        let _ = client.write_point(point1, None, None).await.unwrap();
-        let _ = client.drop_measurement("test3").await.unwrap();
-        let _ = client.drop_database(client.get_db().as_str()).await.unwrap();
+        client.create_database(client.get_db()).await.unwrap();
+        let point = Point::new("test3").add_field("foo", Value::String("bar".to_string()));
+        let point1 = point.clone();
+        let point = point.add_timestamp(1_508_981_970);
+        let point1 = point1.add_timestamp(1_508_982_026);
+
+        client.write_point(point, None, None).await.unwrap();
+        client.query("select * from test3", None).await.unwrap();
+        client.write_point(point1, None, None).await.unwrap();
+        client.drop_measurement("test3").await.unwrap();
+        client.drop_database(client.get_db()).await.unwrap();
     });
 }
 
 #[test]
 fn use_macro() {
     block_on(async {
-        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 mut client = Client::default().set_authentication("root", "root");
+        client.switch_database("use_macro");
+        client.create_database(client.get_db()).await.unwrap();
+        let point = point!("test4").add_field("foo", Value::String("bar".to_string()));
+        let point1 = point.clone();
+        let point = point.add_timestamp(1_508_981_970);
+        let point1 = point1.add_timestamp(1_508_982_026);
 
         let points = points![point, point1];
-        let _ = client.write_points(points, None, None).await;
+        client.write_points(points, None, None).await.unwrap();
 
-        let _ = client.query("select * from test4", None).await.unwrap();
+        client.query("select * from test4", None).await.unwrap();
 
-        let _ = client.drop_measurement("test4").await.unwrap();
+        client.drop_measurement("test4").await.unwrap();
     });
 }
 
 #[test]
 fn use_udp() {
     block_on(async {
-        let mut udp = UdpClient::new("127.0.0.1:8089");
-        udp.add_host("127.0.0.1:8090");
+        let mut udp = UdpClient::new("127.0.0.1:8089".parse().unwrap());
+        udp.add_host("127.0.0.1:8090".parse().unwrap());
         let mut client = Client::default().set_authentication("root", "root");
 
-        let mut point = point!("test");
-        point.add_field("foo", Value::String(String::from("bar")));
+        let point = point!("test").add_field("foo", Value::String(String::from("bar")));
 
-        let _ = udp.write_point(point).unwrap();
+        udp.write_point(point).unwrap();
 
-        let _ = sleep(Duration::from_secs(1));
+        sleep(Duration::from_secs(1));
         client.switch_database("udp");
-        let _ = client.drop_measurement("test").await.unwrap();
+        client.drop_measurement("test").await.unwrap();
         client.switch_database("telegraf");
-        let _ = client.drop_measurement("test").await.unwrap();
+        client.drop_measurement("test").await.unwrap();
     });
 }
 
@@ -244,23 +236,25 @@ bind-address = "127.0.0.1:{rpc_port}"
 
     let cert = reqwest::Certificate::from_pem(&ca_cert_buffer).unwrap();
 
-    let http_client = reqwest::Client::builder().add_root_certificate(cert).build().unwrap();
+    let http_client = reqwest::Client::builder()
+        .add_root_certificate(cert)
+        .build()
+        .unwrap();
 
     let host = format!("https://localhost:{}", http_port);
     let client = Client::new_with_client(host.as_str(), "test_use_https", http_client);
 
     block_on(async {
-        let _ = client.create_database(client.get_db().as_str()).await.unwrap();
+        client.create_database(client.get_db()).await.unwrap();
 
-        let mut point = point!("foo");
-        point.add_field("foo", Value::String(String::from("bar")));
+        let point = point!("foo").add_field("foo", Value::String(String::from("bar")));
 
-        let _ = client.write_point(point, None, None).await.unwrap();
+        client.write_point(point, None, None).await.unwrap();
 
-        let _ = client.query("select * from foo", None).await.unwrap();
+        client.query("select * from foo", None).await.unwrap();
 
-        let _ = client.drop_measurement("foo").await.unwrap();
-        let _ = client.drop_database(client.get_db().as_str()).await.unwrap();
+        client.drop_measurement("foo").await.unwrap();
+        client.drop_database(client.get_db()).await.unwrap();
     });
 
     influxdb_server.kill().unwrap();