Vitaly Buka | 4615e0d | 2015-10-14 15:35:12 -0700 | [diff] [blame] | 1 | // Copyright 2015 The Weave Authors. All rights reserved. |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Stefan Sauer | 2d16dfa | 2015-09-25 17:08:35 +0200 | [diff] [blame] | 5 | #include "src/commands/schema_utils.h" |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 6 | |
| 7 | #include <algorithm> |
| 8 | #include <set> |
| 9 | #include <string> |
| 10 | |
| 11 | #include <base/json/json_writer.h> |
Vitaly Buka | 0d50107 | 2015-08-18 18:09:46 -0700 | [diff] [blame] | 12 | #include <base/logging.h> |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 13 | |
Stefan Sauer | 2d16dfa | 2015-09-25 17:08:35 +0200 | [diff] [blame] | 14 | #include "src/commands/object_schema.h" |
| 15 | #include "src/commands/prop_types.h" |
| 16 | #include "src/commands/prop_values.h" |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 17 | |
Vitaly Buka | b6f015a | 2015-07-09 14:59:23 -0700 | [diff] [blame] | 18 | namespace weave { |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 19 | namespace { |
| 20 | // Helper function to report "type mismatch" errors when parsing JSON. |
Alex Vakulenko | 7e8df46 | 2015-07-07 10:59:20 -0700 | [diff] [blame] | 21 | void ReportJsonTypeMismatch(const tracked_objects::Location& location, |
| 22 | const base::Value* value_in, |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 23 | const std::string& expected_type, |
Vitaly Buka | 0801a1f | 2015-08-14 10:03:46 -0700 | [diff] [blame] | 24 | ErrorPtr* error) { |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 25 | std::string value_as_string; |
Alex Vakulenko | ae1ffbc | 2015-06-15 12:53:22 -0700 | [diff] [blame] | 26 | base::JSONWriter::Write(*value_in, &value_as_string); |
Vitaly Buka | 0801a1f | 2015-08-14 10:03:46 -0700 | [diff] [blame] | 27 | Error::AddToPrintf(error, location, errors::commands::kDomain, |
| 28 | errors::commands::kTypeMismatch, |
| 29 | "Unable to convert value %s into %s", |
| 30 | value_as_string.c_str(), expected_type.c_str()); |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 31 | } |
| 32 | |
| 33 | // Template version of ReportJsonTypeMismatch that deduces the type of expected |
| 34 | // data from the value_out parameter passed to particular overload of |
| 35 | // TypedValueFromJson() function. Always returns false. |
Vitaly Buka | a647c85 | 2015-07-06 14:51:01 -0700 | [diff] [blame] | 36 | template <typename T> |
Alex Vakulenko | 7e8df46 | 2015-07-07 10:59:20 -0700 | [diff] [blame] | 37 | bool ReportUnexpectedJson(const tracked_objects::Location& location, |
| 38 | const base::Value* value_in, |
Vitaly Buka | a647c85 | 2015-07-06 14:51:01 -0700 | [diff] [blame] | 39 | T*, |
Vitaly Buka | 0801a1f | 2015-08-14 10:03:46 -0700 | [diff] [blame] | 40 | ErrorPtr* error) { |
Alex Vakulenko | 7e8df46 | 2015-07-07 10:59:20 -0700 | [diff] [blame] | 41 | ReportJsonTypeMismatch(location, value_in, |
| 42 | PropType::GetTypeStringFromType(GetValueType<T>()), |
| 43 | error); |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 44 | return false; |
| 45 | } |
| 46 | |
Vitaly Buka | 0801a1f | 2015-08-14 10:03:46 -0700 | [diff] [blame] | 47 | bool ErrorMissingProperty(ErrorPtr* error, |
Alex Vakulenko | 7e8df46 | 2015-07-07 10:59:20 -0700 | [diff] [blame] | 48 | const tracked_objects::Location& location, |
| 49 | const char* param_name) { |
Vitaly Buka | 0801a1f | 2015-08-14 10:03:46 -0700 | [diff] [blame] | 50 | Error::AddToPrintf(error, location, errors::commands::kDomain, |
| 51 | errors::commands::kPropertyMissing, |
| 52 | "Required parameter missing: %s", param_name); |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 53 | return false; |
| 54 | } |
Vitaly Buka | 8d8d219 | 2015-07-21 22:25:09 -0700 | [diff] [blame] | 55 | |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 56 | } // namespace |
| 57 | |
| 58 | // Specializations of TypedValueToJson<T>() for supported C++ types. |
Vitaly Buka | 6942e1f | 2015-07-28 15:33:55 -0700 | [diff] [blame] | 59 | std::unique_ptr<base::FundamentalValue> TypedValueToJson(bool value) { |
Vitaly Buka | 39ef195 | 2015-07-21 20:32:11 -0700 | [diff] [blame] | 60 | return std::unique_ptr<base::FundamentalValue>( |
| 61 | new base::FundamentalValue(value)); |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 62 | } |
| 63 | |
Vitaly Buka | 6942e1f | 2015-07-28 15:33:55 -0700 | [diff] [blame] | 64 | std::unique_ptr<base::FundamentalValue> TypedValueToJson(int value) { |
Vitaly Buka | 39ef195 | 2015-07-21 20:32:11 -0700 | [diff] [blame] | 65 | return std::unique_ptr<base::FundamentalValue>( |
| 66 | new base::FundamentalValue(value)); |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 67 | } |
| 68 | |
Vitaly Buka | 6942e1f | 2015-07-28 15:33:55 -0700 | [diff] [blame] | 69 | std::unique_ptr<base::FundamentalValue> TypedValueToJson(double value) { |
Vitaly Buka | 39ef195 | 2015-07-21 20:32:11 -0700 | [diff] [blame] | 70 | return std::unique_ptr<base::FundamentalValue>( |
| 71 | new base::FundamentalValue(value)); |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 72 | } |
| 73 | |
Vitaly Buka | 6942e1f | 2015-07-28 15:33:55 -0700 | [diff] [blame] | 74 | std::unique_ptr<base::StringValue> TypedValueToJson(const std::string& value) { |
Vitaly Buka | 39ef195 | 2015-07-21 20:32:11 -0700 | [diff] [blame] | 75 | return std::unique_ptr<base::StringValue>(new base::StringValue(value)); |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 76 | } |
| 77 | |
Vitaly Buka | 6942e1f | 2015-07-28 15:33:55 -0700 | [diff] [blame] | 78 | std::unique_ptr<base::DictionaryValue> TypedValueToJson(const ValueMap& value) { |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 79 | std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue); |
| 80 | for (const auto& pair : value) { |
Vitaly Buka | 6942e1f | 2015-07-28 15:33:55 -0700 | [diff] [blame] | 81 | auto prop_value = pair.second->ToJson(); |
| 82 | CHECK(prop_value); |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 83 | dict->SetWithoutPathExpansion(pair.first, prop_value.release()); |
| 84 | } |
Vitaly Buka | 39ef195 | 2015-07-21 20:32:11 -0700 | [diff] [blame] | 85 | return dict; |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 86 | } |
| 87 | |
Vitaly Buka | 6942e1f | 2015-07-28 15:33:55 -0700 | [diff] [blame] | 88 | std::unique_ptr<base::ListValue> TypedValueToJson(const ValueVector& value) { |
Alex Vakulenko | 29e6444 | 2015-03-20 13:59:19 -0700 | [diff] [blame] | 89 | std::unique_ptr<base::ListValue> list(new base::ListValue); |
| 90 | for (const auto& item : value) { |
Vitaly Buka | 6942e1f | 2015-07-28 15:33:55 -0700 | [diff] [blame] | 91 | auto json = item->ToJson(); |
| 92 | CHECK(json); |
Alex Vakulenko | 29e6444 | 2015-03-20 13:59:19 -0700 | [diff] [blame] | 93 | list->Append(json.release()); |
| 94 | } |
Vitaly Buka | 39ef195 | 2015-07-21 20:32:11 -0700 | [diff] [blame] | 95 | return list; |
Alex Vakulenko | 29e6444 | 2015-03-20 13:59:19 -0700 | [diff] [blame] | 96 | } |
| 97 | |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 98 | bool TypedValueFromJson(const base::Value* value_in, |
Alex Vakulenko | d94656e | 2015-03-18 09:54:37 -0700 | [diff] [blame] | 99 | const PropType* type, |
| 100 | bool* value_out, |
Vitaly Buka | 0801a1f | 2015-08-14 10:03:46 -0700 | [diff] [blame] | 101 | ErrorPtr* error) { |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 102 | return value_in->GetAsBoolean(value_out) || |
Alex Vakulenko | 7e8df46 | 2015-07-07 10:59:20 -0700 | [diff] [blame] | 103 | ReportUnexpectedJson(FROM_HERE, value_in, value_out, error); |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 104 | } |
| 105 | |
| 106 | bool TypedValueFromJson(const base::Value* value_in, |
Alex Vakulenko | d94656e | 2015-03-18 09:54:37 -0700 | [diff] [blame] | 107 | const PropType* type, |
| 108 | int* value_out, |
Vitaly Buka | 0801a1f | 2015-08-14 10:03:46 -0700 | [diff] [blame] | 109 | ErrorPtr* error) { |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 110 | return value_in->GetAsInteger(value_out) || |
Alex Vakulenko | 7e8df46 | 2015-07-07 10:59:20 -0700 | [diff] [blame] | 111 | ReportUnexpectedJson(FROM_HERE, value_in, value_out, error); |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 112 | } |
| 113 | |
| 114 | bool TypedValueFromJson(const base::Value* value_in, |
Alex Vakulenko | d94656e | 2015-03-18 09:54:37 -0700 | [diff] [blame] | 115 | const PropType* type, |
| 116 | double* value_out, |
Vitaly Buka | 0801a1f | 2015-08-14 10:03:46 -0700 | [diff] [blame] | 117 | ErrorPtr* error) { |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 118 | return value_in->GetAsDouble(value_out) || |
Alex Vakulenko | 7e8df46 | 2015-07-07 10:59:20 -0700 | [diff] [blame] | 119 | ReportUnexpectedJson(FROM_HERE, value_in, value_out, error); |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 120 | } |
| 121 | |
| 122 | bool TypedValueFromJson(const base::Value* value_in, |
Alex Vakulenko | d94656e | 2015-03-18 09:54:37 -0700 | [diff] [blame] | 123 | const PropType* type, |
| 124 | std::string* value_out, |
Vitaly Buka | 0801a1f | 2015-08-14 10:03:46 -0700 | [diff] [blame] | 125 | ErrorPtr* error) { |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 126 | return value_in->GetAsString(value_out) || |
Alex Vakulenko | 7e8df46 | 2015-07-07 10:59:20 -0700 | [diff] [blame] | 127 | ReportUnexpectedJson(FROM_HERE, value_in, value_out, error); |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 128 | } |
| 129 | |
| 130 | bool TypedValueFromJson(const base::Value* value_in, |
Alex Vakulenko | d94656e | 2015-03-18 09:54:37 -0700 | [diff] [blame] | 131 | const PropType* type, |
Vitaly Buka | 774cdf5 | 2015-07-21 13:55:00 -0700 | [diff] [blame] | 132 | ValueMap* value_out, |
Vitaly Buka | 0801a1f | 2015-08-14 10:03:46 -0700 | [diff] [blame] | 133 | ErrorPtr* error) { |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 134 | const base::DictionaryValue* dict = nullptr; |
| 135 | if (!value_in->GetAsDictionary(&dict)) |
Alex Vakulenko | 7e8df46 | 2015-07-07 10:59:20 -0700 | [diff] [blame] | 136 | return ReportUnexpectedJson(FROM_HERE, value_in, value_out, error); |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 137 | |
Alex Vakulenko | d94656e | 2015-03-18 09:54:37 -0700 | [diff] [blame] | 138 | CHECK(type) << "Object definition must be provided"; |
| 139 | CHECK(ValueType::Object == type->GetType()) << "Type must be Object"; |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 140 | |
Alex Vakulenko | d94656e | 2015-03-18 09:54:37 -0700 | [diff] [blame] | 141 | const ObjectSchema* object_schema = type->GetObject()->GetObjectSchemaPtr(); |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 142 | std::set<std::string> keys_processed; |
Alex Vakulenko | 3e864c0 | 2015-03-24 10:19:32 -0700 | [diff] [blame] | 143 | value_out->clear(); // Clear possible default values already in |value_out|. |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 144 | for (const auto& pair : object_schema->GetProps()) { |
| 145 | const PropValue* def_value = pair.second->GetDefaultValue(); |
| 146 | if (dict->HasKey(pair.first)) { |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 147 | const base::Value* param_value = nullptr; |
| 148 | CHECK(dict->GetWithoutPathExpansion(pair.first, ¶m_value)) |
| 149 | << "Unable to get parameter"; |
Vitaly Buka | 79c05e9 | 2015-07-29 12:25:37 -0700 | [diff] [blame] | 150 | auto value = pair.second->CreatePropValue(*param_value, error); |
| 151 | if (!value) { |
Vitaly Buka | 0801a1f | 2015-08-14 10:03:46 -0700 | [diff] [blame] | 152 | Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain, |
| 153 | errors::commands::kInvalidPropValue, |
| 154 | "Invalid value for property '%s'", |
| 155 | pair.first.c_str()); |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 156 | return false; |
Alex Vakulenko | a32d83a | 2014-09-19 15:05:24 -0700 | [diff] [blame] | 157 | } |
Vitaly Buka | 52d006a | 2015-11-21 17:14:51 -0800 | [diff] [blame] | 158 | value_out->insert(std::make_pair(pair.first, std::move(value))); |
Alex Vakulenko | 7e8df46 | 2015-07-07 10:59:20 -0700 | [diff] [blame] | 159 | keys_processed.insert(pair.first); |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 160 | } else if (def_value) { |
Vitaly Buka | 52d006a | 2015-11-21 17:14:51 -0800 | [diff] [blame] | 161 | value_out->insert(std::make_pair(pair.first, def_value->Clone())); |
Alex Vakulenko | 7e8df46 | 2015-07-07 10:59:20 -0700 | [diff] [blame] | 162 | keys_processed.insert(pair.first); |
| 163 | } else if (pair.second->IsRequired()) { |
| 164 | return ErrorMissingProperty(error, FROM_HERE, pair.first.c_str()); |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 165 | } |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 166 | } |
| 167 | |
| 168 | // Just for sanity, make sure that we processed all the necessary properties |
| 169 | // and there weren't any extra (unknown) ones specified. If so, ignore |
| 170 | // them, but log as warnings... |
| 171 | base::DictionaryValue::Iterator iter(*dict); |
| 172 | while (!iter.IsAtEnd()) { |
| 173 | std::string key = iter.key(); |
| 174 | if (keys_processed.find(key) == keys_processed.end() && |
| 175 | !object_schema->GetExtraPropertiesAllowed()) { |
Vitaly Buka | 0801a1f | 2015-08-14 10:03:46 -0700 | [diff] [blame] | 176 | Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain, |
| 177 | errors::commands::kUnknownProperty, |
| 178 | "Unrecognized parameter '%s'", key.c_str()); |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 179 | return false; |
| 180 | } |
| 181 | iter.Advance(); |
| 182 | } |
| 183 | |
| 184 | // Now go over all property values and validate them. |
| 185 | for (const auto& pair : *value_out) { |
| 186 | const PropType* prop_type = pair.second->GetPropType(); |
| 187 | CHECK(prop_type) << "Value property type must be available"; |
| 188 | if (!prop_type->ValidateConstraints(*pair.second, error)) { |
Vitaly Buka | 0801a1f | 2015-08-14 10:03:46 -0700 | [diff] [blame] | 189 | Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain, |
| 190 | errors::commands::kInvalidPropValue, |
| 191 | "Invalid value for property '%s'", pair.first.c_str()); |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 192 | return false; |
| 193 | } |
| 194 | } |
| 195 | return true; |
| 196 | } |
| 197 | |
Alex Vakulenko | 29e6444 | 2015-03-20 13:59:19 -0700 | [diff] [blame] | 198 | bool TypedValueFromJson(const base::Value* value_in, |
| 199 | const PropType* type, |
Vitaly Buka | 774cdf5 | 2015-07-21 13:55:00 -0700 | [diff] [blame] | 200 | ValueVector* value_out, |
Vitaly Buka | 0801a1f | 2015-08-14 10:03:46 -0700 | [diff] [blame] | 201 | ErrorPtr* error) { |
Alex Vakulenko | 29e6444 | 2015-03-20 13:59:19 -0700 | [diff] [blame] | 202 | const base::ListValue* list = nullptr; |
| 203 | if (!value_in->GetAsList(&list)) |
Alex Vakulenko | 7e8df46 | 2015-07-07 10:59:20 -0700 | [diff] [blame] | 204 | return ReportUnexpectedJson(FROM_HERE, value_in, value_out, error); |
Alex Vakulenko | 29e6444 | 2015-03-20 13:59:19 -0700 | [diff] [blame] | 205 | |
| 206 | CHECK(type) << "Array type definition must be provided"; |
| 207 | CHECK(ValueType::Array == type->GetType()) << "Type must be Array"; |
| 208 | const PropType* item_type = type->GetArray()->GetItemTypePtr(); |
| 209 | CHECK(item_type) << "Incomplete array type definition"; |
| 210 | |
Alex Vakulenko | 3e864c0 | 2015-03-24 10:19:32 -0700 | [diff] [blame] | 211 | // This value might already contain values from the type defaults. |
| 212 | // Clear them first before proceeding. |
| 213 | value_out->clear(); |
Alex Vakulenko | 29e6444 | 2015-03-20 13:59:19 -0700 | [diff] [blame] | 214 | value_out->reserve(list->GetSize()); |
| 215 | for (const base::Value* item : *list) { |
Vitaly Buka | 79c05e9 | 2015-07-29 12:25:37 -0700 | [diff] [blame] | 216 | std::unique_ptr<PropValue> prop_value = |
| 217 | item_type->CreatePropValue(*item, error); |
| 218 | if (!prop_value) |
Alex Vakulenko | 29e6444 | 2015-03-20 13:59:19 -0700 | [diff] [blame] | 219 | return false; |
Alex Vakulenko | 29e6444 | 2015-03-20 13:59:19 -0700 | [diff] [blame] | 220 | value_out->push_back(std::move(prop_value)); |
| 221 | } |
| 222 | return true; |
| 223 | } |
| 224 | |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 225 | // Compares two sets of key-value pairs from two Objects. |
Vitaly Buka | 774cdf5 | 2015-07-21 13:55:00 -0700 | [diff] [blame] | 226 | static bool obj_cmp(const ValueMap::value_type& v1, |
| 227 | const ValueMap::value_type& v2) { |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 228 | return (v1.first == v2.first) && v1.second->IsEqual(v2.second.get()); |
| 229 | } |
| 230 | |
Vitaly Buka | 774cdf5 | 2015-07-21 13:55:00 -0700 | [diff] [blame] | 231 | bool operator==(const ValueMap& obj1, const ValueMap& obj2) { |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 232 | if (obj1.size() != obj2.size()) |
| 233 | return false; |
| 234 | |
| 235 | auto pair = std::mismatch(obj1.begin(), obj1.end(), obj2.begin(), obj_cmp); |
| 236 | return pair == std::make_pair(obj1.end(), obj2.end()); |
| 237 | } |
| 238 | |
Vitaly Buka | 774cdf5 | 2015-07-21 13:55:00 -0700 | [diff] [blame] | 239 | bool operator==(const ValueVector& arr1, const ValueVector& arr2) { |
Alex Vakulenko | 29e6444 | 2015-03-20 13:59:19 -0700 | [diff] [blame] | 240 | if (arr1.size() != arr2.size()) |
| 241 | return false; |
| 242 | |
Vitaly Buka | 774cdf5 | 2015-07-21 13:55:00 -0700 | [diff] [blame] | 243 | using Type = const ValueVector::value_type; |
Alex Vakulenko | 29e6444 | 2015-03-20 13:59:19 -0700 | [diff] [blame] | 244 | // Compare two array items. |
| 245 | auto arr_cmp = [](const Type& v1, const Type& v2) { |
| 246 | return v1->IsEqual(v2.get()); |
| 247 | }; |
| 248 | auto pair = std::mismatch(arr1.begin(), arr1.end(), arr2.begin(), arr_cmp); |
| 249 | return pair == std::make_pair(arr1.end(), arr2.end()); |
| 250 | } |
| 251 | |
Vitaly Buka | 774cdf5 | 2015-07-21 13:55:00 -0700 | [diff] [blame] | 252 | std::string ToString(const ValueMap& obj) { |
Vitaly Buka | 6942e1f | 2015-07-28 15:33:55 -0700 | [diff] [blame] | 253 | auto val = TypedValueToJson(obj); |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 254 | std::string str; |
Alex Vakulenko | ae1ffbc | 2015-06-15 12:53:22 -0700 | [diff] [blame] | 255 | base::JSONWriter::Write(*val, &str); |
Alex Vakulenko | 66ec292 | 2014-06-17 15:30:22 -0700 | [diff] [blame] | 256 | return str; |
| 257 | } |
| 258 | |
Vitaly Buka | 774cdf5 | 2015-07-21 13:55:00 -0700 | [diff] [blame] | 259 | std::string ToString(const ValueVector& arr) { |
Vitaly Buka | 6942e1f | 2015-07-28 15:33:55 -0700 | [diff] [blame] | 260 | auto val = TypedValueToJson(arr); |
Alex Vakulenko | 29e6444 | 2015-03-20 13:59:19 -0700 | [diff] [blame] | 261 | std::string str; |
Alex Vakulenko | ae1ffbc | 2015-06-15 12:53:22 -0700 | [diff] [blame] | 262 | base::JSONWriter::Write(*val, &str); |
Alex Vakulenko | 29e6444 | 2015-03-20 13:59:19 -0700 | [diff] [blame] | 263 | return str; |
| 264 | } |
| 265 | |
Vitaly Buka | b6f015a | 2015-07-09 14:59:23 -0700 | [diff] [blame] | 266 | } // namespace weave |