buffet: load standard GCD command definitions in CommandManager

Changed CommandManager to load the base command definitions
and use it as a base schema for loading device-specific commands
and make sure device vendors are not redefining standard commands
in breaking manner.

BUG=chromium:374861
TEST=USE=buffet P2_TEST_FILTER="buffet::*" FEATURES=test emerge-link platform2

Change-Id: I5f2d5500bc90ef918a8a6df1242bdd1fe3b78615
Reviewed-on: https://chromium-review.googlesource.com/209175
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/commands/command_manager.cc b/buffet/commands/command_manager.cc
index fc04b2d..d7cf34c 100644
--- a/buffet/commands/command_manager.cc
+++ b/buffet/commands/command_manager.cc
@@ -4,10 +4,78 @@
 
 #include "buffet/commands/command_manager.h"
 
+#include <base/file_util.h>
+#include <base/values.h>
+#include <base/json/json_reader.h>
+
+#include "buffet/commands/schema_constants.h"
+#include "buffet/error_codes.h"
+
 namespace buffet {
 
 const CommandDictionary& CommandManager::GetCommandDictionary() const {
   return dictionary_;
 }
 
+bool CommandManager::LoadBaseCommands(const base::DictionaryValue& json,
+                                      ErrorPtr* error) {
+  return base_dictionary_.LoadCommands(json, "", nullptr, error);
+}
+
+bool CommandManager::LoadBaseCommands(const base::FilePath& json_file_path,
+                                      ErrorPtr* error) {
+  std::unique_ptr<const base::DictionaryValue> json = LoadJsonDict(
+      json_file_path, error);
+  if (!json)
+    return false;
+  return LoadBaseCommands(*json, error);
+}
+
+bool CommandManager::LoadCommands(const base::DictionaryValue& json,
+                                  const std::string& category,
+                                  ErrorPtr* error) {
+  return dictionary_.LoadCommands(json, category, &base_dictionary_, error);
+}
+
+bool CommandManager::LoadCommands(const base::FilePath& json_file_path,
+                                  ErrorPtr* error) {
+  std::unique_ptr<const base::DictionaryValue> json = LoadJsonDict(
+      json_file_path, error);
+  if (!json)
+    return false;
+  std::string category = json_file_path.BaseName().RemoveExtension().value();
+  return LoadCommands(*json, category, error);
+}
+
+std::unique_ptr<const base::DictionaryValue> CommandManager::LoadJsonDict(
+    const base::FilePath& json_file_path, ErrorPtr* error) {
+  std::string json_string;
+  if (!base::ReadFileToString(json_file_path, &json_string)) {
+    Error::AddToPrintf(error, errors::file_system::kDomain,
+                       errors::file_system::kFileReadError,
+                       "Failed to read file '%s'",
+                       json_file_path.value().c_str());
+    return std::unique_ptr<const base::DictionaryValue>();
+  }
+  std::string error_message;
+  base::Value* value = base::JSONReader::ReadAndReturnError(
+      json_string, base::JSON_PARSE_RFC, nullptr, &error_message);
+  if (!value) {
+    Error::AddToPrintf(error, errors::json::kDomain, errors::json::kParseError,
+                       "Error parsing content of JSON file '%s': %s",
+                       json_file_path.value().c_str(), error_message.c_str());
+    return std::unique_ptr<const base::DictionaryValue>();
+  }
+  const base::DictionaryValue* dict_value = nullptr;
+  if (!value->GetAsDictionary(&dict_value)) {
+    delete value;
+    Error::AddToPrintf(error, errors::json::kDomain,
+                       errors::json::kObjectExpected,
+                       "Content of file '%s' is not a JSON object",
+                       json_file_path.value().c_str());
+    return std::unique_ptr<const base::DictionaryValue>();
+  }
+  return std::unique_ptr<const base::DictionaryValue>(dict_value);
+}
+
 }  // namespace buffet