Buffet: utility function tweaks

Various tweakes to helper functions and classes, split off from
a larger CL that required them.

1. Added Error::AddToPrintf to help add formatted error messages.
2. Added Error::GetFirstError to get the innermost error occurred.
3. Added string_utils::ToString and swept code using std::to_string
   in order to ensure we format doubles correctly (using %g instead
   of %f format specifier) and bool values (using "true"/"false"
   instead of 1/0).
4. Fixed C-style cast in http_utils.h and using static_cast now.
5. Fixed a few linter warnings. Also since the linter was updated
   there is no reason to have some NOLINT since many C++11 features
   are now recognized properly by cpplint.

BUG=None
TEST=All unit tests pass.

Change-Id: I208ffaa3f0ec0a5ff78bf9e8151e784ec8cd77e2
Reviewed-on: https://chromium-review.googlesource.com/202962
Tested-by: Alex Vakulenko <avakulenko@chromium.org>
Reviewed-by: Christopher Wiley <wiley@chromium.org>
Commit-Queue: Alex Vakulenko <avakulenko@chromium.org>
diff --git a/buffet/device_registration_info.cc b/buffet/device_registration_info.cc
index 4aab715..5447726 100644
--- a/buffet/device_registration_info.cc
+++ b/buffet/device_registration_info.cc
@@ -66,9 +66,7 @@
     const std::string& access_token) {
   std::string authorization =
       buffet::string_utils::Join(' ', access_token_type, access_token);
-  // Linter doesn't like the ; after } on the following line...
-  return {buffet::http::request_header::kAuthorization,
-          authorization};  // NOLINT
+  return {buffet::http::request_header::kAuthorization, authorization};
 }
 
 std::unique_ptr<base::DictionaryValue> ParseOAuthResponse(
@@ -320,8 +318,8 @@
   if (!param_value.empty())
     return true;
 
-  Error::AddTo(error, kErrorDomainBuffet, "missing_parameter",
-               "Parameter " + param_name + " not specified");
+  Error::AddToPrintf(error, kErrorDomainBuffet, "missing_parameter",
+                     "Parameter %s not specified", param_name.c_str());
   return false;
 }
 
@@ -364,9 +362,9 @@
   set_device_configuration_params->Append(param1);
 
   base::ListValue* vendor_commands = new base::ListValue;
-  for (auto&& pair : commands) {
+  for (const auto& pair : commands) {
     base::ListValue* params = new base::ListValue;
-    for (auto&& param_name : pair.second) {
+    for (const auto& param_name : pair.second) {
       base::DictionaryValue* param = new base::DictionaryValue;
       param->SetString("name", param_name);
       params->Append(param);
diff --git a/buffet/error.cc b/buffet/error.cc
index bccb9b8..db5543d 100644
--- a/buffet/error.cc
+++ b/buffet/error.cc
@@ -5,6 +5,7 @@
 #include "buffet/error.h"
 
 #include <base/logging.h>
+#include <base/strings/stringprintf.h>
 
 using buffet::Error;
 using buffet::ErrorPtr;
@@ -36,6 +37,15 @@
   }
 }
 
+void Error::AddToPrintf(ErrorPtr* error, const std::string& domain,
+                        const std::string& code, const char* format, ...) {
+  va_list ap;
+  va_start(ap, format);
+  std::string message = base::StringPrintV(format, ap);
+  va_end(ap);
+  AddTo(error, domain, code, message);
+}
+
 bool Error::HasDomain(const std::string& domain) const {
   const Error* err = this;
   while (err) {
@@ -56,6 +66,13 @@
   return false;
 }
 
+const Error* Error::GetFirstError() const {
+  const Error* err = this;
+  while (err->GetInnerError())
+    err = err->GetInnerError();
+  return err;
+}
+
 Error::Error(const std::string& domain, const std::string& code,
              const std::string& message, ErrorPtr inner_error) :
     domain_(domain), code_(code), message_(message),
diff --git a/buffet/error.h b/buffet/error.h
index 2f5ba66..4e90c2b 100644
--- a/buffet/error.h
+++ b/buffet/error.h
@@ -30,6 +30,11 @@
   // the error chain pointed to by |error|.
   static void AddTo(ErrorPtr* error, const std::string& domain,
                     const std::string& code, const std::string& message);
+  // Same as the Error::AddTo above, but allows to pass in a printf-like
+  // format string and optional parameters to format the error message.
+  static void AddToPrintf(ErrorPtr* error, const std::string& domain,
+                          const std::string& code,
+                          const char* format, ...) PRINTF_FORMAT(4, 5);
 
   // Returns the error domain, code and message
   const std::string& GetDomain() const { return domain_; }
@@ -46,6 +51,10 @@
   // Gets a pointer to the inner error, if present. Returns nullptr otherwise.
   const Error* GetInnerError() const { return inner_error_.get(); }
 
+  // Gets a pointer to the first error occurred.
+  // Returns itself if no inner error are available.
+  const Error* GetFirstError() const;
+
  protected:
   // Constructor is protected since this object is supposed to be
   // created via the Create factory methods.
diff --git a/buffet/http_connection_curl.cc b/buffet/http_connection_curl.cc
index 0dae74c..ebdd3cc 100644
--- a/buffet/http_connection_curl.cc
+++ b/buffet/http_connection_curl.cc
@@ -121,7 +121,7 @@
   if (header_list)
     curl_slist_free_all(header_list);
   if (ret != CURLE_OK) {
-    Error::AddTo(error, http::curl::kErrorDomain, std::to_string(ret),
+    Error::AddTo(error, http::curl::kErrorDomain, string_utils::ToString(ret),
                  curl_easy_strerror(ret));
   } else {
     LOG(INFO) << "Response: " << GetResponseStatusCode() << " ("
diff --git a/buffet/http_connection_fake.cc b/buffet/http_connection_fake.cc
index 87fbf23..383f6d4 100644
--- a/buffet/http_connection_fake.cc
+++ b/buffet/http_connection_fake.cc
@@ -37,7 +37,7 @@
 
 bool Connection::FinishRequest(ErrorPtr* error) {
   request_.AddHeaders({{request_header::kContentLength,
-                      std::to_string(request_.GetData().size())}});
+                      string_utils::ToString(request_.GetData().size())}});
   fake::Transport* transport = static_cast<fake::Transport*>(transport_.get());
   CHECK(transport) << "Expecting a fake transport";
   auto handler = transport->GetHandler(request_.GetURL(), request_.GetMethod());
diff --git a/buffet/http_request.cc b/buffet/http_request.cc
index df09ca4..c08e797 100644
--- a/buffet/http_request.cc
+++ b/buffet/http_request.cc
@@ -193,11 +193,11 @@
               p.second != range_value_omitted) {
             std::string range;
             if (p.first != range_value_omitted) {
-              range = std::to_string(p.first);
+              range = string_utils::ToString(p.first);
             }
             range += '-';
             if (p.second != range_value_omitted) {
-              range += std::to_string(p.second);
+              range += string_utils::ToString(p.second);
             }
             ranges.push_back(range);
           }
diff --git a/buffet/http_request.h b/buffet/http_request.h
index 8aedc45..e5b9710 100644
--- a/buffet/http_request.h
+++ b/buffet/http_request.h
@@ -5,6 +5,7 @@
 #ifndef BUFFET_HTTP_REQUEST_H_
 #define BUFFET_HTTP_REQUEST_H_
 
+#include <limits>
 #include <map>
 #include <memory>
 #include <string>
@@ -303,7 +304,7 @@
   // range_value_omitted is used in |ranges_| list to indicate omitted value.
   // E.g. range (10,range_value_omitted) represents bytes from 10 to the end
   // of the data stream.
-  const uint64_t range_value_omitted = (uint64_t)-1;
+  const uint64_t range_value_omitted = std::numeric_limits<uint64_t>::max();
 
   DISALLOW_COPY_AND_ASSIGN(Request);
 };
diff --git a/buffet/http_transport_fake.cc b/buffet/http_transport_fake.cc
index 0e7dfc5..471de11 100644
--- a/buffet/http_transport_fake.cc
+++ b/buffet/http_transport_fake.cc
@@ -14,6 +14,7 @@
 #include "buffet/http_connection_fake.h"
 #include "buffet/http_request.h"
 #include "buffet/mime_utils.h"
+#include "buffet/string_utils.h"
 #include "buffet/url_utils.h"
 
 namespace buffet {
@@ -128,7 +129,7 @@
 }
 
 void ServerRequestResponseBase::AddHeaders(const HeaderList& headers) {
-  for (auto&& pair : headers) {
+  for (const auto& pair : headers) {
     if (pair.second.empty())
       headers_.erase(pair.first);
     else
@@ -170,7 +171,7 @@
   status_code_ = status_code;
   AddData(data, data_size);
   AddHeaders({
-    {response_header::kContentLength, std::to_string(data_size)},
+    {response_header::kContentLength, string_utils::ToString(data_size)},
     {response_header::kContentType, mime_type}
   });
 }
@@ -194,7 +195,7 @@
 void ServerResponse::ReplyJson(int status_code,
                                const http::FormFieldList& fields) {
   base::DictionaryValue json;
-  for (auto&& pair : fields) {
+  for (const auto& pair : fields) {
     json.SetString(pair.first, pair.second);
   }
   ReplyJson(status_code, &json);
@@ -250,7 +251,7 @@
     {505, "HTTP Version Not Supported"},
   };
 
-  for (auto&& pair : status_text_map) {
+  for (const auto& pair : status_text_map) {
     if (pair.first == status_code_)
       return pair.second;
   }
diff --git a/buffet/http_utils_unittest.cc b/buffet/http_utils_unittest.cc
index fc86d31..404ef7b 100644
--- a/buffet/http_utils_unittest.cc
+++ b/buffet/http_utils_unittest.cc
@@ -28,13 +28,13 @@
                             fake::ServerResponse* response) {
   response->Reply(status_code::Ok, request.GetData(),
                   request.GetHeader(request_header::kContentType).c_str());
-};
+}
 
 // Returns the request method as a plain text response.
 static void EchoMethodHandler(const fake::ServerRequest& request,
                               fake::ServerResponse* response) {
   response->ReplyText(status_code::Ok, request.GetMethod(), mime::text::kPlain);
-};
+}
 
 ///////////////////////////////////////////////////////////////////////////////
 TEST(HttpUtils, SendRequest_BinaryData) {
@@ -43,7 +43,7 @@
                         base::Bind(EchoDataHandler));
 
   // Test binary data round-tripping.
-  std::vector<unsigned char> custom_data({0xFF, 0x00, 0x80, 0x40, 0xC0, 0x7F});
+  std::vector<unsigned char> custom_data{0xFF, 0x00, 0x80, 0x40, 0xC0, 0x7F};
   auto response = http::SendRequest(request_type::kPost, kEchoUrl,
                                     custom_data.data(), custom_data.size(),
                                     mime::application::kOctet_stream,
@@ -114,7 +114,7 @@
     base::DictionaryValue json;
     json.SetString("method", request.GetMethod());
     json.SetString("data", request.GetDataAsString());
-    for (auto&& pair : request.GetHeaders()) {
+    for (const auto& pair : request.GetHeaders()) {
       json.SetString("header." + pair.first, pair.second);
     }
     response->ReplyJson(status_code::Ok, &json);
@@ -204,7 +204,7 @@
     EXPECT_EQ("256", request.GetHeader(request_header::kContentLength));
     EXPECT_EQ(mime::application::kOctet_stream,
               request.GetHeader(request_header::kContentType));
-    auto&& data = request.GetData();
+    const auto& data = request.GetData();
     EXPECT_EQ(256, data.size());
 
     // Sum up all the bytes.
@@ -218,8 +218,7 @@
 
   /// Fill the data buffer with bytes from 0x00 to 0xFF.
   std::vector<unsigned char> data(256);
-  unsigned char counter = 0xFF;
-  std::generate(data.begin(), data.end(), [&counter]() { return ++counter; });
+  std::iota(data.begin(), data.end(), 0);
 
   auto response = http::PostBinary(kFakeUrl, data.data(), data.size(),
                                    transport, nullptr);
@@ -311,7 +310,7 @@
   transport->AddHandler(kFakeUrl, request_type::kPost, base::Bind(JsonHandler));
 
   // Test valid JSON responses (with success or error codes).
-  for (auto&& item : {"200;data", "400;wrong", "500;Internal Server error"}) {
+  for (auto item : {"200;data", "400;wrong", "500;Internal Server error"}) {
     auto pair = string_utils::SplitAtFirst(item, ';');
     auto response = http::PostFormData(kFakeUrl, {
                       {"code", pair.first},
@@ -322,7 +321,7 @@
     EXPECT_NE(nullptr, json.get());
     std::string value;
     EXPECT_TRUE(json->GetString("data", &value));
-    EXPECT_EQ(pair.first, std::to_string(code));
+    EXPECT_EQ(pair.first, string_utils::ToString(code));
     EXPECT_EQ(pair.second, value);
   }
 
diff --git a/buffet/string_utils.cc b/buffet/string_utils.cc
index 55c7d3f..8916e96 100644
--- a/buffet/string_utils.cc
+++ b/buffet/string_utils.cc
@@ -9,6 +9,7 @@
 #include <utility>
 
 #include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
 
 namespace buffet {
 namespace string_utils {
@@ -36,7 +37,7 @@
 
   if (trim_whitespaces) {
     std::for_each(tokens.begin(), tokens.end(),
-                  [](std::string& str) {  // NOLINT(runtime/references)
+                  [](std::string& str) {
       base::TrimWhitespaceASCII(str, base::TRIM_ALL, &str); });
   }
 
@@ -86,5 +87,13 @@
   return str1 + delimiter + str2;
 }
 
+std::string ToString(double value) {
+  return base::StringPrintf("%g", value);
+}
+
+std::string ToString(bool value) {
+  return value ? "true" : "false";
+}
+
 }  // namespace string_utils
 }  // namespace buffet
diff --git a/buffet/string_utils.h b/buffet/string_utils.h
index 95da72d..e29675b 100644
--- a/buffet/string_utils.h
+++ b/buffet/string_utils.h
@@ -36,6 +36,20 @@
 std::string Join(const std::string& delimiter,
                  const std::string& str1, const std::string& str2);
 
+// string_utils::ToString() is a helper function to convert any scalar type
+// to a string. In most cases, it redirects the call to std::to_string with
+// two exceptions: for std::string itself and for double and bool.
+template<typename T>
+inline std::string ToString(T value) { return std::to_string(value); }
+// Having the following overload is handy for templates where the type
+// of template parameter isn't known and could be a string itself.
+inline std::string ToString(std::string value) { return value; }
+// We overload this for double because std::to_string(double) uses %f to
+// format the value and I would like to use a shorter %g format instead.
+std::string ToString(double value);
+// And the bool to be converted as true/false instead of 1/0.
+std::string ToString(bool value);
+
 }  // namespace string_utils
 }  // namespace buffet