libweave: Partially copy string_utils from libchromeos To move dependency on libchromeos. BUG=brillo:1257 TEST=`FEATURES=test emerge-gizmo libweave buffet Change-Id: Ie9e4bb1a754373c26ed6497cdb3ab03df8a78d87 Reviewed-on: https://chromium-review.googlesource.com/293613 Reviewed-by: Vitaly Buka <vitalybuka@chromium.org> Commit-Queue: Vitaly Buka <vitalybuka@chromium.org> Tested-by: Vitaly Buka <vitalybuka@chromium.org>
diff --git a/libweave/libweave.gyp b/libweave/libweave.gyp index 8903b28..3ff064b 100644 --- a/libweave/libweave.gyp +++ b/libweave/libweave.gyp
@@ -75,6 +75,7 @@ 'src/states/state_manager.cc', 'src/states/state_package.cc', 'src/storage_impls.cc', + 'src/string_utils.cc', 'src/utils.cc', ], }, @@ -150,6 +151,7 @@ 'src/states/state_change_queue_unittest.cc', 'src/states/state_manager_unittest.cc', 'src/states/state_package_unittest.cc', + 'src/string_utils_unittest.cc', 'src/weave_testrunner.cc', ], },
diff --git a/libweave/src/buffet_config.cc b/libweave/src/buffet_config.cc index 97ae16e..2698630 100644 --- a/libweave/src/buffet_config.cc +++ b/libweave/src/buffet_config.cc
@@ -9,11 +9,11 @@ #include <base/files/file_util.h> #include <base/logging.h> #include <base/strings/string_number_conversions.h> -#include <chromeos/strings/string_utils.h> #include <weave/enum_to_string.h> #include "libweave/src/storage_impls.h" #include "libweave/src/storage_interface.h" +#include "libweave/src/string_utils.h" namespace { @@ -195,8 +195,7 @@ std::string modes_str; if (store.GetString(config_keys::kPairingModes, &modes_str)) { std::set<PairingType> pairing_modes; - for (const std::string& mode : - chromeos::string_utils::Split(modes_str, ",", true, true)) { + for (const std::string& mode : Split(modes_str, ",", true, true)) { PairingType pairing_mode; CHECK(StringToEnum(mode, &pairing_mode)); pairing_modes.insert(pairing_mode);
diff --git a/libweave/src/commands/command_definition.cc b/libweave/src/commands/command_definition.cc index e14a182..66a487c 100644 --- a/libweave/src/commands/command_definition.cc +++ b/libweave/src/commands/command_definition.cc
@@ -7,9 +7,9 @@ #include <vector> #include <chromeos/errors/error.h> -#include <chromeos/strings/string_utils.h> #include "libweave/src/commands/schema_constants.h" +#include "libweave/src/string_utils.h" namespace weave { @@ -29,7 +29,7 @@ if (str == commands::attributes::kCommand_Visibility_None) return true; - for (const std::string& value : chromeos::string_utils::Split(str, ",")) { + for (const std::string& value : Split(str, ",", true, true)) { if (value == commands::attributes::kCommand_Visibility_Local) { local = true; } else if (value == commands::attributes::kCommand_Visibility_Cloud) {
diff --git a/libweave/src/commands/command_dictionary.cc b/libweave/src/commands/command_dictionary.cc index 02c3ddd..2f36850 100644 --- a/libweave/src/commands/command_dictionary.cc +++ b/libweave/src/commands/command_dictionary.cc
@@ -5,11 +5,11 @@ #include "libweave/src/commands/command_dictionary.h" #include <base/values.h> -#include <chromeos/strings/string_utils.h> #include <weave/enum_to_string.h> #include "libweave/src/commands/command_definition.h" #include "libweave/src/commands/schema_constants.h" +#include "libweave/src/string_utils.h" namespace weave { @@ -64,8 +64,7 @@ return false; } // Construct the compound command name as "pkg_name.cmd_name". - std::string full_command_name = - chromeos::string_utils::Join(".", package_name, command_name); + std::string full_command_name = Join(".", package_name, command_name); const ObjectSchema* base_parameters_def = nullptr; const ObjectSchema* base_progress_def = nullptr; @@ -226,9 +225,10 @@ CHECK(parameters); // Progress and results are not part of public commandDefs. - auto cmd_name_parts = chromeos::string_utils::SplitAtFirst(pair.first, "."); - std::string package_name = cmd_name_parts.first; - std::string command_name = cmd_name_parts.second; + auto parts = SplitAtFirst(pair.first, ".", true); + const std::string& package_name = parts.first; + const std::string& command_name = parts.second; + base::DictionaryValue* package = nullptr; if (!dict->GetDictionaryWithoutPathExpansion(package_name, &package)) { // If this is the first time we encounter this package, create a JSON
diff --git a/libweave/src/commands/command_queue_unittest.cc b/libweave/src/commands/command_queue_unittest.cc index cd91c3d..84eeb3d 100644 --- a/libweave/src/commands/command_queue_unittest.cc +++ b/libweave/src/commands/command_queue_unittest.cc
@@ -10,11 +10,11 @@ #include <base/bind.h> #include <base/memory/weak_ptr.h> -#include <chromeos/strings/string_utils.h> #include <gtest/gtest.h> #include "libweave/src/commands/command_definition.h" #include "libweave/src/commands/object_schema.h" +#include "libweave/src/string_utils.h" namespace weave { @@ -72,7 +72,6 @@ // Get the comma-separated list of command IDs currently accumulated in the // command queue_. std::string GetIDs() const { - using chromeos::string_utils::Join; return Join(",", std::vector<std::string>(ids_.begin(), ids_.end())); } @@ -136,8 +135,8 @@ queue_.Add(CreateDummyCommandInstance("base.reboot", id1)); queue_.Add(CreateDummyCommandInstance("base.reboot", id2)); std::set<std::string> ids{id1, id2}; // Make sure they are sorted properly. - std::string expected_set = chromeos::string_utils::Join( - ",", std::vector<std::string>(ids.begin(), ids.end())); + std::string expected_set = + Join(",", std::vector<std::string>(ids.begin(), ids.end())); EXPECT_EQ(expected_set, dispatch.GetIDs()); Remove(id1); EXPECT_EQ(id2, dispatch.GetIDs());
diff --git a/libweave/src/commands/object_schema.cc b/libweave/src/commands/object_schema.cc index b08d143..b2c6307 100644 --- a/libweave/src/commands/object_schema.cc +++ b/libweave/src/commands/object_schema.cc
@@ -14,6 +14,7 @@ #include "libweave/src/commands/prop_types.h" #include "libweave/src/commands/prop_values.h" #include "libweave/src/commands/schema_constants.h" +#include "libweave/src/string_utils.h" namespace weave { @@ -23,10 +24,10 @@ // Generates an error if the string identifies an unknown type. std::unique_ptr<PropType> CreatePropType(const std::string& type_name, chromeos::ErrorPtr* error) { - std::string primary_type; - std::string array_type; - chromeos::string_utils::SplitAtFirst(type_name, ".", &primary_type, - &array_type, false); + auto parts = SplitAtFirst(type_name, ".", false); + const std::string& primary_type = parts.first; + const std::string& array_type = parts.second; + std::unique_ptr<PropType> prop; ValueType type; if (PropType::GetTypeFromTypeString(primary_type, &type)) {
diff --git a/libweave/src/commands/prop_constraints.cc b/libweave/src/commands/prop_constraints.cc index 4f730f7..0b77ec5 100644 --- a/libweave/src/commands/prop_constraints.cc +++ b/libweave/src/commands/prop_constraints.cc
@@ -8,6 +8,7 @@ #include "libweave/src/commands/prop_values.h" #include "libweave/src/commands/schema_constants.h" +#include "libweave/src/string_utils.h" namespace weave { @@ -54,11 +55,10 @@ bool Constraint::ReportErrorNotOneOf(chromeos::ErrorPtr* error, const std::string& val, const std::vector<std::string>& values) { - chromeos::Error::AddToPrintf( - error, FROM_HERE, errors::commands::kDomain, - errors::commands::kOutOfRange, - "Value %s is invalid. Expected one of [%s]", val.c_str(), - chromeos::string_utils::Join(",", values).c_str()); + chromeos::Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain, + errors::commands::kOutOfRange, + "Value %s is invalid. Expected one of [%s]", + val.c_str(), Join(",", values).c_str()); return false; }
diff --git a/libweave/src/commands/prop_constraints.h b/libweave/src/commands/prop_constraints.h index 098f97c..c0744d5 100644 --- a/libweave/src/commands/prop_constraints.h +++ b/libweave/src/commands/prop_constraints.h
@@ -12,11 +12,11 @@ #include <base/macros.h> #include <base/values.h> #include <chromeos/errors/error.h> -#include <chromeos/strings/string_utils.h> #include "libweave/src/commands/prop_values.h" #include "libweave/src/commands/schema_constants.h" #include "libweave/src/commands/schema_utils.h" +#include "libweave/src/string_utils.h" namespace weave { @@ -134,9 +134,8 @@ chromeos::ErrorPtr* error) const override { const T& v = static_cast<const TypedValueBase<T>&>(value).GetValue(); if (v < this->limit_.value) { - return this->ReportErrorLessThan( - error, chromeos::string_utils::ToString(v), - chromeos::string_utils::ToString(this->limit_.value)); + return this->ReportErrorLessThan(error, std::to_string(v), + std::to_string(this->limit_.value)); } return true; } @@ -176,9 +175,8 @@ chromeos::ErrorPtr* error) const override { const T& v = static_cast<const TypedValueBase<T>&>(value).GetValue(); if (v > this->limit_.value) - return this->ReportErrorGreaterThan( - error, chromeos::string_utils::ToString(v), - chromeos::string_utils::ToString(this->limit_.value)); + return this->ReportErrorGreaterThan(error, std::to_string(v), + std::to_string(this->limit_.value)); return true; }
diff --git a/libweave/src/commands/prop_types.cc b/libweave/src/commands/prop_types.cc index 6daabc0..5a77610 100644 --- a/libweave/src/commands/prop_types.cc +++ b/libweave/src/commands/prop_types.cc
@@ -11,7 +11,6 @@ #include <base/json/json_writer.h> #include <base/logging.h> #include <base/values.h> -#include <chromeos/strings/string_utils.h> #include "libweave/src/commands/object_schema.h" #include "libweave/src/commands/prop_values.h"
diff --git a/libweave/src/data_encoding.cc b/libweave/src/data_encoding.cc index 419e2d4..28a63ea 100644 --- a/libweave/src/data_encoding.cc +++ b/libweave/src/data_encoding.cc
@@ -9,8 +9,8 @@ #include <base/logging.h> #include <base/strings/string_util.h> #include <base/strings/stringprintf.h> -#include <chromeos/strings/string_utils.h> +#include "libweave/src/string_utils.h" #include "libweave/third_party/modp_b64/modp_b64/modp_b64.h" namespace weave { @@ -90,17 +90,16 @@ for (const auto& p : params) { std::string key = UrlEncode(p.first.c_str(), encodeSpaceAsPlus); std::string value = UrlEncode(p.second.c_str(), encodeSpaceAsPlus); - pairs.push_back(chromeos::string_utils::Join("=", key, value)); + pairs.push_back(Join("=", key, value)); } - return chromeos::string_utils::Join("&", pairs); + return Join("&", pairs); } WebParamList WebParamsDecode(const std::string& data) { WebParamList result; - std::vector<std::string> params = chromeos::string_utils::Split(data, "&"); - for (const auto& p : params) { - auto pair = chromeos::string_utils::SplitAtFirst(p, "="); + for (const auto& p : Split(data, "&", true, true)) { + auto pair = SplitAtFirst(p, "=", true); result.emplace_back(UrlDecode(pair.first.c_str()), UrlDecode(pair.second.c_str())); }
diff --git a/libweave/src/device_registration_info.cc b/libweave/src/device_registration_info.cc index a799c41..3573cb8 100644 --- a/libweave/src/device_registration_info.cc +++ b/libweave/src/device_registration_info.cc
@@ -16,7 +16,6 @@ #include <base/strings/string_number_conversions.h> #include <base/values.h> #include <chromeos/key_value_store.h> -#include <chromeos/strings/string_utils.h> #include <weave/http_client.h> #include <weave/network.h> #include <weave/task_runner.h> @@ -31,6 +30,7 @@ #include "libweave/src/json_error_codes.h" #include "libweave/src/notification/xmpp_channel.h" #include "libweave/src/states/state_manager.h" +#include "libweave/src/string_utils.h" #include "libweave/src/utils.h" namespace weave { @@ -175,9 +175,8 @@ chromeos::ErrorPtr* error) { // Make sure we have a correct content type. Do not try to parse // binary files, or HTML output. Limit to application/json and text/plain. - auto content_type = - chromeos::string_utils::SplitAtFirst(response.GetContentType(), ";") - .first; + std::string content_type = + SplitAtFirst(response.GetContentType(), ";", true).first; if (content_type != http::kJson && content_type != http::kPlain) { chromeos::Error::AddTo(error, FROM_HERE, errors::json::kDomain, @@ -890,8 +889,7 @@ EnumToString(CommandStatus::kAborted)); if (error) { command_patch.SetString(commands::attributes::kCommand_ErrorCode, - chromeos::string_utils::Join( - ":", error->GetDomain(), error->GetCode())); + Join(":", error->GetDomain(), error->GetCode())); std::vector<std::string> messages; const chromeos::Error* current_error = error.get(); while (current_error) { @@ -899,7 +897,7 @@ current_error = current_error->GetInnerError(); } command_patch.SetString(commands::attributes::kCommand_ErrorMessage, - chromeos::string_utils::Join(";", messages)); + Join(";", messages)); } UpdateCommand(command_id, command_patch, base::Bind(&base::DoNothing), base::Bind(&base::DoNothing));
diff --git a/libweave/src/notification/xml_node.cc b/libweave/src/notification/xml_node.cc index 6550ddc..f8b6c52 100644 --- a/libweave/src/notification/xml_node.cc +++ b/libweave/src/notification/xml_node.cc
@@ -5,7 +5,8 @@ #include "libweave/src/notification/xml_node.h" #include <base/strings/stringprintf.h> -#include <chromeos/strings/string_utils.h> + +#include "libweave/src/string_utils.h" namespace weave { @@ -61,10 +62,9 @@ const std::string& name_path, bool recursive, std::vector<const XmlNode*>* children) const { - std::string name; - std::string rest_of_path; - chromeos::string_utils::SplitAtFirst(name_path, "/", &name, &rest_of_path, - false); + auto parts = SplitAtFirst(name_path, "/", false); + const std::string& name = parts.first; + const std::string& rest_of_path = parts.second; for (const auto& child : children_) { const XmlNode* found_node = nullptr; if (child->name() == name) {
diff --git a/libweave/src/privet/privet_handler.cc b/libweave/src/privet/privet_handler.cc index 04fc8d2..9f22750 100644 --- a/libweave/src/privet/privet_handler.cc +++ b/libweave/src/privet/privet_handler.cc
@@ -15,7 +15,6 @@ #include <base/strings/string_number_conversions.h> #include <base/strings/stringprintf.h> #include <base/values.h> -#include <chromeos/strings/string_utils.h> #include <weave/enum_to_string.h> #include "libweave/src/http_constants.h" @@ -25,6 +24,7 @@ #include "libweave/src/privet/identity_delegate.h" #include "libweave/src/privet/security_delegate.h" #include "libweave/src/privet/wifi_delegate.h" +#include "libweave/src/string_utils.h" namespace weave { namespace privet { @@ -155,10 +155,7 @@ } std::string GetAuthTokenFromAuthHeader(const std::string& auth_header) { - std::string name; - std::string value; - chromeos::string_utils::SplitAtFirst(auth_header, " ", &name, &value); - return value; + return SplitAtFirst(auth_header, " ", true).second; } std::unique_ptr<base::DictionaryValue> ErrorInfoToJson(
diff --git a/libweave/src/privet/privet_manager.cc b/libweave/src/privet/privet_manager.cc index 7daa0f9..c64430a 100644 --- a/libweave/src/privet/privet_manager.cc +++ b/libweave/src/privet/privet_manager.cc
@@ -17,7 +17,6 @@ #include <base/strings/string_number_conversions.h> #include <base/values.h> #include <chromeos/key_value_store.h> -#include <chromeos/strings/string_utils.h> #include <weave/network.h> #include "libweave/src/device_registration_info.h" @@ -27,6 +26,7 @@ #include "libweave/src/privet/device_delegate.h" #include "libweave/src/privet/privet_handler.h" #include "libweave/src/privet/publisher.h" +#include "libweave/src/string_utils.h" namespace weave { namespace privet { @@ -123,9 +123,7 @@ const base::DictionaryValue* dictionary = ∅ std::string content_type = - chromeos::string_utils::SplitAtFirst( - request.GetFirstHeader(http::kContentType), ";") - .first; + SplitAtFirst(request.GetFirstHeader(http::kContentType), ";", true).first; if (content_type == http::kJson) { value.reset(base::JSONReader::Read(data).release()); if (value)
diff --git a/libweave/src/privet/publisher.cc b/libweave/src/privet/publisher.cc index 367dbd8..b9f7080 100644 --- a/libweave/src/privet/publisher.cc +++ b/libweave/src/privet/publisher.cc
@@ -7,15 +7,13 @@ #include <map> #include <chromeos/errors/error.h> -#include <chromeos/strings/string_utils.h> #include <weave/mdns.h> #include "libweave/src/privet/cloud_delegate.h" #include "libweave/src/privet/device_delegate.h" #include "libweave/src/privet/wifi_bootstrap_manager.h" #include "libweave/src/privet/wifi_ssid_generator.h" - -using chromeos::string_utils::Join; +#include "libweave/src/string_utils.h" namespace weave { namespace privet {
diff --git a/libweave/src/privet/security_manager.cc b/libweave/src/privet/security_manager.cc index 2125744..1343a7c 100644 --- a/libweave/src/privet/security_manager.cc +++ b/libweave/src/privet/security_manager.cc
@@ -18,13 +18,13 @@ #include <base/strings/stringprintf.h> #include <base/time/time.h> #include <chromeos/key_value_store.h> -#include <chromeos/strings/string_utils.h> #include <weave/task_runner.h> #include "libweave/external/crypto/p224_spake.h" #include "libweave/src/data_encoding.h" #include "libweave/src/privet/constants.h" #include "libweave/src/privet/openssl_utils.h" +#include "libweave/src/string_utils.h" namespace weave { namespace privet { @@ -49,7 +49,7 @@ // Splits string of "scope:id:time" format. UserInfo SplitTokenData(const std::string& token, base::Time* time) { const UserInfo kNone; - auto parts = chromeos::string_utils::Split(token, kTokenDelimeter); + auto parts = Split(token, kTokenDelimeter, false, false); if (parts.size() != 3) return kNone; int scope = 0; @@ -300,7 +300,7 @@ // simultaneously and implement throttling to avoid brute force attack. if (!on_start_.is_null()) { on_start_.Run(session, mode, - chromeos::string_utils::GetStringAsBytes(code)); + std::vector<uint8_t>{code.begin(), code.end()}); } return true;
diff --git a/libweave/src/privet/security_manager_unittest.cc b/libweave/src/privet/security_manager_unittest.cc index a6ea288..0d13cfe 100644 --- a/libweave/src/privet/security_manager_unittest.cc +++ b/libweave/src/privet/security_manager_unittest.cc
@@ -19,7 +19,6 @@ #include <base/strings/string_number_conversions.h> #include <base/strings/string_util.h> #include <chromeos/key_value_store.h> -#include <chromeos/strings/string_utils.h> #include "libweave/external/crypto/p224_spake.h" #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -102,7 +101,7 @@ std::vector<uint8_t> device_commitment; ASSERT_TRUE(Base64Decode(device_commitment_base64, &device_commitment)); spake.ProcessMessage( - chromeos::string_utils::GetBytesAsString(device_commitment)); + std::string(device_commitment.begin(), device_commitment.end())); const std::string& key = spake.GetUnverifiedKey(); std::vector<uint8_t> auth_code{
diff --git a/libweave/src/states/state_manager.cc b/libweave/src/states/state_manager.cc index fa8f81e..6f2646b 100644 --- a/libweave/src/states/state_manager.cc +++ b/libweave/src/states/state_manager.cc
@@ -9,11 +9,11 @@ #include <base/logging.h> #include <base/values.h> #include <chromeos/key_value_store.h> -#include <chromeos/strings/string_utils.h> #include "libweave/src/json_error_codes.h" #include "libweave/src/states/error_codes.h" #include "libweave/src/states/state_change_queue_interface.h" +#include "libweave/src/string_utils.h" #include "libweave/src/utils.h" namespace weave { @@ -108,10 +108,11 @@ const base::Value& value, const base::Time& timestamp, chromeos::ErrorPtr* error) { - std::string package_name; - std::string property_name; - bool split = chromeos::string_utils::SplitAtFirst( - full_property_name, ".", &package_name, &property_name); + auto parts = SplitAtFirst(full_property_name, ".", true); + const std::string& package_name = parts.first; + const std::string& property_name = parts.second; + const bool split = (full_property_name.find(".") != std::string::npos); + if (full_property_name.empty() || (split && property_name.empty())) { chromeos::Error::AddTo(error, FROM_HERE, errors::state::kDomain, errors::state::kPropertyNameMissing,
diff --git a/libweave/src/string_utils.cc b/libweave/src/string_utils.cc new file mode 100644 index 0000000..d73e508 --- /dev/null +++ b/libweave/src/string_utils.cc
@@ -0,0 +1,74 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <algorithm> +#include <string.h> +#include <utility> + +#include <base/strings/string_util.h> + +#include "libweave/src/string_utils.h" + +namespace weave { + +namespace { + +bool SplitAtFirst(const std::string& str, + const std::string& delimiter, + std::string* left_part, + std::string* right_part, + bool trim_whitespaces) { + bool delimiter_found = false; + std::string::size_type pos = str.find(delimiter); + if (pos != std::string::npos) { + *left_part = str.substr(0, pos); + *right_part = str.substr(pos + delimiter.size()); + delimiter_found = true; + } else { + *left_part = str; + right_part->clear(); + } + + if (trim_whitespaces) { + base::TrimWhitespaceASCII(*left_part, base::TRIM_ALL, left_part); + base::TrimWhitespaceASCII(*right_part, base::TRIM_ALL, right_part); + } + + return delimiter_found; +} + +} // namespace + +std::vector<std::string> Split(const std::string& str, + const std::string& delimiter, + bool trim_whitespaces, + bool purge_empty_strings) { + std::vector<std::string> tokens; + if (str.empty()) + return tokens; + + for (std::string::size_type i = 0;;) { + const std::string::size_type pos = + delimiter.empty() ? (i + 1) : str.find(delimiter, i); + std::string tmp_str{str.substr(i, pos - i)}; + if (trim_whitespaces) + base::TrimWhitespaceASCII(tmp_str, base::TRIM_ALL, &tmp_str); + if (!tmp_str.empty() || !purge_empty_strings) + tokens.emplace_back(std::move(tmp_str)); + if (pos >= str.size()) + break; + i = pos + delimiter.size(); + } + return tokens; +} + +std::pair<std::string, std::string> SplitAtFirst(const std::string& str, + const std::string& delimiter, + bool trim_whitespaces) { + std::pair<std::string, std::string> pair; + SplitAtFirst(str, delimiter, &pair.first, &pair.second, trim_whitespaces); + return pair; +} + +} // namespace weave
diff --git a/libweave/src/string_utils.h b/libweave/src/string_utils.h new file mode 100644 index 0000000..5771971 --- /dev/null +++ b/libweave/src/string_utils.h
@@ -0,0 +1,61 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef LIBWEAVE_SRC_STRING_UTILS_H_ +#define LIBWEAVE_SRC_STRING_UTILS_H_ + +#include <string> +#include <utility> +#include <vector> + +namespace weave { + +// Treats the string as a delimited list of substrings and returns the array +// of original elements of the list. +// |trim_whitespaces| causes each element to have all whitespaces trimmed off. +// |purge_empty_strings| specifies whether empty elements from the original +// string should be omitted. +std::vector<std::string> Split(const std::string& str, + const std::string& delimiter, + bool trim_whitespaces, + bool purge_empty_strings); + +// Splits the string into two pieces at the first position of the specified +// delimiter. +std::pair<std::string, std::string> SplitAtFirst(const std::string& str, + const std::string& delimiter, + bool trim_whitespaces); + +// Joins strings into a single string separated by |delimiter|. +template <class InputIterator> +std::string JoinRange(const std::string& delimiter, + InputIterator first, + InputIterator last) { + std::string result; + if (first == last) + return result; + result = *first; + for (++first; first != last; ++first) { + result += delimiter; + result += *first; + } + return result; +} + +template <class Container> +std::string Join(const std::string& delimiter, const Container& strings) { + using std::begin; + using std::end; + return JoinRange(delimiter, begin(strings), end(strings)); +} + +inline std::string Join(const std::string& delimiter, + const std::string& str1, + const std::string& str2) { + return str1 + delimiter + str2; +} + +} // namespace weave + +#endif // LIBWEAVE_SRC_STRING_UTILS_H_
diff --git a/libweave/src/string_utils_unittest.cc b/libweave/src/string_utils_unittest.cc new file mode 100644 index 0000000..083ebb8 --- /dev/null +++ b/libweave/src/string_utils_unittest.cc
@@ -0,0 +1,139 @@ +// Copyright (c) 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "libweave/src/string_utils.h" + +#include <list> +#include <set> +#include <string> +#include <vector> + +#include <gtest/gtest.h> + +namespace weave { + +TEST(StringUtils, Split) { + std::vector<std::string> parts; + + parts = Split("", ",", false, false); + EXPECT_EQ(0, parts.size()); + + parts = Split("abc", ",", false, false); + EXPECT_EQ(1, parts.size()); + EXPECT_EQ("abc", parts[0]); + + parts = Split(",a,bc , d, ,e, ", ",", true, true); + EXPECT_EQ(4, parts.size()); + EXPECT_EQ("a", parts[0]); + EXPECT_EQ("bc", parts[1]); + EXPECT_EQ("d", parts[2]); + EXPECT_EQ("e", parts[3]); + + parts = Split(",a,bc , d, ,e, ", ",", false, true); + EXPECT_EQ(6, parts.size()); + EXPECT_EQ("a", parts[0]); + EXPECT_EQ("bc ", parts[1]); + EXPECT_EQ(" d", parts[2]); + EXPECT_EQ(" ", parts[3]); + EXPECT_EQ("e", parts[4]); + EXPECT_EQ(" ", parts[5]); + + parts = Split(",a,bc , d, ,e, ", ",", true, false); + EXPECT_EQ(7, parts.size()); + EXPECT_EQ("", parts[0]); + EXPECT_EQ("a", parts[1]); + EXPECT_EQ("bc", parts[2]); + EXPECT_EQ("d", parts[3]); + EXPECT_EQ("", parts[4]); + EXPECT_EQ("e", parts[5]); + EXPECT_EQ("", parts[6]); + + parts = Split(",a,bc , d, ,e, ", ",", false, false); + EXPECT_EQ(7, parts.size()); + EXPECT_EQ("", parts[0]); + EXPECT_EQ("a", parts[1]); + EXPECT_EQ("bc ", parts[2]); + EXPECT_EQ(" d", parts[3]); + EXPECT_EQ(" ", parts[4]); + EXPECT_EQ("e", parts[5]); + EXPECT_EQ(" ", parts[6]); + + parts = Split("abc:=xyz", ":=", false, false); + EXPECT_EQ(2, parts.size()); + EXPECT_EQ("abc", parts[0]); + EXPECT_EQ("xyz", parts[1]); + + parts = Split("abc", "", false, false); + EXPECT_EQ(3, parts.size()); + EXPECT_EQ("a", parts[0]); + EXPECT_EQ("b", parts[1]); + EXPECT_EQ("c", parts[2]); +} + +TEST(StringUtils, SplitAtFirst) { + std::pair<std::string, std::string> pair; + + pair = SplitAtFirst(" 123 : 4 : 56 : 789 ", ":", true); + EXPECT_EQ("123", pair.first); + EXPECT_EQ("4 : 56 : 789", pair.second); + + pair = SplitAtFirst(" 123 : 4 : 56 : 789 ", ":", false); + EXPECT_EQ(" 123 ", pair.first); + EXPECT_EQ(" 4 : 56 : 789 ", pair.second); + + pair = SplitAtFirst("", "=", true); + EXPECT_EQ("", pair.first); + EXPECT_EQ("", pair.second); + + pair = SplitAtFirst("=", "=", true); + EXPECT_EQ("", pair.first); + EXPECT_EQ("", pair.second); + + pair = SplitAtFirst("a=", "=", true); + EXPECT_EQ("a", pair.first); + EXPECT_EQ("", pair.second); + + pair = SplitAtFirst("abc=", "=", true); + EXPECT_EQ("abc", pair.first); + EXPECT_EQ("", pair.second); + + pair = SplitAtFirst("=a", "=", true); + EXPECT_EQ("", pair.first); + EXPECT_EQ("a", pair.second); + + pair = SplitAtFirst("=abc=", "=", true); + EXPECT_EQ("", pair.first); + EXPECT_EQ("abc=", pair.second); + + pair = SplitAtFirst("abc", "=", true); + EXPECT_EQ("abc", pair.first); + EXPECT_EQ("", pair.second); + + pair = SplitAtFirst("abc:=xyz", ":=", true); + EXPECT_EQ("abc", pair.first); + EXPECT_EQ("xyz", pair.second); + + pair = SplitAtFirst("abc", "", true); + EXPECT_EQ("", pair.first); + EXPECT_EQ("abc", pair.second); +} + +TEST(StringUtils, Join_String) { + EXPECT_EQ("", Join(",", std::vector<std::string>{})); + EXPECT_EQ("abc", Join(",", std::vector<std::string>{"abc"})); + EXPECT_EQ("abc,,xyz", Join(",", std::vector<std::string>{"abc", "", "xyz"})); + EXPECT_EQ("abc,defg", Join(",", std::vector<std::string>{"abc", "defg"})); + EXPECT_EQ("1 : 2 : 3", Join(" : ", std::vector<std::string>{"1", "2", "3"})); + EXPECT_EQ("1:2", Join(":", std::set<std::string>{"1", "2"})); + EXPECT_EQ("1:2", Join(":", std::vector<std::string>{"1", "2"})); + EXPECT_EQ("1:2", Join(":", std::list<std::string>{"1", "2"})); + EXPECT_EQ("123", Join("", std::vector<std::string>{"1", "2", "3"})); +} + +TEST(StringUtils, Join_Pair) { + EXPECT_EQ("ab,cd", Join(",", "ab", "cd")); + EXPECT_EQ("key = value", Join(" = ", "key", "value")); +} + +} // namespace weave