You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kudu.apache.org by to...@apache.org on 2016/08/16 06:08:33 UTC
[2/4] kudu git commit: tool: rewrite parser logic
tool: rewrite parser logic
While leaf and non-leaf actions share some common properties, there is much
they don't share. Rather than shoehorn both into the same Action paradigm, I
think it makes more sense to consider them separately.
This patch splits Action into either Mode (non-leaf node) or Action (leaf
node). The common properties are now found in the Label struct.
Additionally, each kind of node is now structured as a class with proper
encapsulation, builders, and other goodies. Overall this simplifies the
command line parsing logic, hides more internal details, and reduces the
boilerplate needed to add a mode or an action.
There's no change to the tool's interface with the outside world.
Change-Id: I794fc527525a57283f0165e262283adf14160def
Reviewed-on: http://gerrit.cloudera.org:8080/3996
Tested-by: Kudu Jenkins
Reviewed-by: Todd Lipcon <to...@apache.org>
Project: http://git-wip-us.apache.org/repos/asf/kudu/repo
Commit: http://git-wip-us.apache.org/repos/asf/kudu/commit/5ef37438
Tree: http://git-wip-us.apache.org/repos/asf/kudu/tree/5ef37438
Diff: http://git-wip-us.apache.org/repos/asf/kudu/diff/5ef37438
Branch: refs/heads/master
Commit: 5ef374388cfe88e7efec26121a2fe02e7d32eba5
Parents: 401985e
Author: Adar Dembo <ad...@cloudera.com>
Authored: Sun Aug 14 22:13:04 2016 -0700
Committer: Todd Lipcon <to...@apache.org>
Committed: Tue Aug 16 05:57:18 2016 +0000
----------------------------------------------------------------------
src/kudu/tools/tool_action.cc | 89 +++++++++++----
src/kudu/tools/tool_action.h | 178 +++++++++++++++++++++++-------
src/kudu/tools/tool_action_fs.cc | 56 +++++-----
src/kudu/tools/tool_action_tablet.cc | 92 ++++++++-------
src/kudu/tools/tool_main.cc | 118 ++++++++++----------
5 files changed, 333 insertions(+), 200 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/kudu/blob/5ef37438/src/kudu/tools/tool_action.cc
----------------------------------------------------------------------
diff --git a/src/kudu/tools/tool_action.cc b/src/kudu/tools/tool_action.cc
index 4daf931..317c162 100644
--- a/src/kudu/tools/tool_action.cc
+++ b/src/kudu/tools/tool_action.cc
@@ -18,6 +18,7 @@
#include "kudu/tools/tool_action.h"
#include <deque>
+#include <memory>
#include <string>
#include <vector>
@@ -26,35 +27,89 @@
using std::deque;
using std::string;
+using std::unique_ptr;
using std::vector;
using strings::Substitute;
namespace kudu {
namespace tools {
-string BuildActionChainString(const vector<Action>& chain) {
- return JoinMapped(chain, [](const Action& a){ return a.name; }, " ");
+namespace {
+
+string BuildUsageString(const vector<Mode*>& chain) {
+ string modes = JoinMapped(chain, [](Mode* a){ return a->name(); }, " ");
+ return Substitute("Usage: $0", modes);
+}
+
+} // anonymous namespace
+
+ModeBuilder::ModeBuilder(const Label& label)
+ : label_(label) {
+}
+
+ModeBuilder& ModeBuilder::AddMode(unique_ptr<Mode> mode) {
+ submodes_.push_back(std::move(mode));
+ return *this;
}
-string BuildUsageString(const vector<Action>& chain) {
- return Substitute("Usage: $0", BuildActionChainString(chain));
+ModeBuilder& ModeBuilder::AddAction(unique_ptr<Action> action) {
+ actions_.push_back(std::move(action));
+ return *this;
}
-string BuildHelpString(const vector<Action>& sub_actions, string usage_str) {
- string msg = Substitute("$0 <action>\n", usage_str);
+unique_ptr<Mode> ModeBuilder::Build() {
+ unique_ptr<Mode> mode(new Mode());
+ mode->label_ = label_;
+ mode->submodes_ = std::move(submodes_);
+ mode->actions_ = std::move(actions_);
+ return mode;
+}
+
+// Get help for this mode, passing in its parent mode chain.
+string Mode::BuildHelp(const vector<Mode*>& chain) const {
+ string msg = Substitute("$0 <action>\n", BuildUsageString(chain));
msg += "Action can be one of the following:\n";
- for (const auto& a : sub_actions) {
- msg += Substitute(" $0 : $1\n", a.name, a.description);
+ for (const auto& m : modes()) {
+ msg += Substitute(" $0 : $1\n", m->name(), m->description());
+ }
+ for (const auto& a : actions()) {
+ msg += Substitute(" $0 : $1\n", a->name(), a->description());
}
return msg;
}
-string BuildLeafActionHelpString(const vector<Action>& chain) {
- DCHECK(!chain.empty());
- Action action = chain.back();
- string msg = Substitute("$0", BuildUsageString(chain));
+Mode::Mode() {
+}
+
+ActionBuilder::ActionBuilder(const Label& label, const ActionRunner& runner)
+ : label_(label),
+ runner_(runner) {
+}
+
+ActionBuilder& ActionBuilder::AddGflag(const string& gflag) {
+ gflags_.push_back(gflag);
+ return *this;
+}
+
+unique_ptr<Action> ActionBuilder::Build() {
+ unique_ptr<Action> action(new Action());
+ action->label_ = label_;
+ action->runner_ = runner_;
+ action->gflags_ = gflags_;
+ return action;
+}
+
+Action::Action() {
+}
+
+Status Action::Run(const vector<Mode*>& chain, deque<string> args) const {
+ return runner_(chain, this, args);
+}
+
+string Action::BuildHelp(const vector<Mode*>& chain) const {
+ string msg = Substitute("$0 $1", BuildUsageString(chain), name());
string gflags_msg;
- for (const auto& gflag : action.gflags) {
+ for (const auto& gflag : gflags_) {
google::CommandLineFlagInfo gflag_info =
google::GetCommandLineFlagInfoOrDie(gflag.c_str());
string noun;
@@ -69,17 +124,11 @@ string BuildLeafActionHelpString(const vector<Action>& chain) {
gflags_msg += google::DescribeOneFlag(gflag_info);
}
msg += "\n";
- msg += Substitute("$0\n", action.description);
+ msg += Substitute("$0\n", label_.description);
msg += gflags_msg;
return msg;
}
-string BuildNonLeafActionHelpString(const vector<Action>& chain) {
- string usage = BuildUsageString(chain);
- DCHECK(!chain.empty());
- return BuildHelpString(chain.back().sub_actions, usage);
-}
-
Status ParseAndRemoveArg(const char* arg_name,
deque<string>* remaining_args,
string* parsed_arg) {
http://git-wip-us.apache.org/repos/asf/kudu/blob/5ef37438/src/kudu/tools/tool_action.h
----------------------------------------------------------------------
diff --git a/src/kudu/tools/tool_action.h b/src/kudu/tools/tool_action.h
index 6e6cc5a..cea7c7e 100644
--- a/src/kudu/tools/tool_action.h
+++ b/src/kudu/tools/tool_action.h
@@ -19,23 +19,39 @@
#include <deque>
#include <glog/logging.h>
+#include <memory>
#include <string>
#include <vector>
+#include "kudu/gutil/strings/join.h"
#include "kudu/gutil/strings/substitute.h"
#include "kudu/util/status.h"
namespace kudu {
namespace tools {
-// Encapsulates all knowledge for a particular tool action.
+class Action;
+class Mode;
+
+// The command line tool is structured as a tree with two kinds of nodes: modes
+// and actions. Actions are leaf nodes, each representing a particular
+// operation that the tool can take. Modes are non-leaf nodes that are
+// basically just intuitive groupings of actions.
+//
+// Regardless of type, every node has a name which is used to match it against
+// a command line argument during parsing. Additionally, every node has a
+// description, displayed (along with the name) in help text.
//
-// All actions are arranged in a tree. Leaf actions are invokable: they will
-// do something meaningful when run() is called. Non-leaf actions do not have
-// a run(); an attempt to invoke them will yield help() instead.
+// Every node (be it action or mode) has pointers to its children, but not to
+// its parent mode. As such, operations that require information from the
+// parent modes expect the caller to provide those modes as a "mode chain".
//
-// Sample action tree:
+// Sample node tree:
//
+// root
+// |
+// |
+// |
// fs
// | |
// +--+ +--+
@@ -43,50 +59,127 @@ namespace tools {
// format print_uuid
//
// Given this tree:
-// - "<program> fs" will print some text explaining all of fs's actions.
+// - "<program> fs" will show all of fs's possible actions.
// - "<program> fs format" will format a filesystem.
// - "<program> fs print_uuid" will print a filesystem's UUID.
-struct Action {
- // The name of the action (e.g. "fs").
+
+// Properties common to all nodes.
+struct Label {
+ // The node's name (e.g. "fs"). Uniquely identifies the node action amongst
+ // its siblings in the tree.
std::string name;
- // The description of the action (e.g. "Operate on a local Kudu filesystem").
+ // The node's description (e.g. "Operate on a local Kudu filesystem").
std::string description;
+};
- // Invokes an action, passing in the complete action chain and all remaining
- // command line arguments. The arguments are passed by value so that the
- // function can modify them if need be.
- std::function<Status(const std::vector<Action>&,
- std::deque<std::string>)> run;
+// Builds a new mode (non-leaf) node.
+class ModeBuilder {
+ public:
+ // Creates a new ModeBuilder with a specific label.
+ explicit ModeBuilder(const Label& label);
- // Get help for an action, passing in the complete action chain.
- std::function<std::string(const std::vector<Action>&)> help;
+ // Adds a new mode (non-leaf child node) to this builder.
+ ModeBuilder& AddMode(std::unique_ptr<Mode> mode);
- // This action's children.
- std::vector<Action> sub_actions;
+ // Adds a new action (leaf child node) to this builder.
+ ModeBuilder& AddAction(std::unique_ptr<Action> action);
- // This action's gflags (if any).
- std::vector<std::string> gflags;
+ // Creates a mode using builder state.
+ //
+ // May only be called once.
+ std::unique_ptr<Mode> Build();
+
+ private:
+ const Label label_;
+
+ std::vector<std::unique_ptr<Mode>> submodes_;
+
+ std::vector<std::unique_ptr<Action>> actions_;
};
-// Constructs a string with the names of all actions in the chain
-// (e.g. "<program> fs format").
-std::string BuildActionChainString(const std::vector<Action>& chain);
+// A non-leaf node in the tree, representing a logical grouping for actions or
+// more modes.
+class Mode {
+ public:
+
+ // Returns the help for this mode given its parent mode chain.
+ std::string BuildHelp(const std::vector<Mode*>& chain) const;
-// Constructs a usage string (e.g. "Usage: <program> fs format").
-std::string BuildUsageString(const std::vector<Action>& chain);
+ const std::string& name() const { return label_.name; }
+
+ const std::string& description() const { return label_.description; }
+
+ const std::vector<std::unique_ptr<Mode>>& modes() const { return submodes_; }
+
+ const std::vector<std::unique_ptr<Action>>& actions() const { return actions_; }
+
+ private:
+ friend class ModeBuilder;
+
+ Mode();
+
+ Label label_;
+
+ std::vector<std::unique_ptr<Mode>> submodes_;
+
+ std::vector<std::unique_ptr<Action>> actions_;
+};
-// Constructs a help string suitable for leaf actions.
-std::string BuildLeafActionHelpString(const std::vector<Action>& chain);
+// Function signature for any operation represented by an action. When run, the
+// operation receives the parent mode chain, the current action, and any
+// remaining command line arguments.
+typedef std::function<Status(const std::vector<Mode*>&,
+ const Action*,
+ std::deque<std::string>)> ActionRunner;
-// Constructs a help string suitable for non-leaf actions.
-std::string BuildNonLeafActionHelpString(const std::vector<Action>& chain);
+// Builds a new action (leaf) node.
+class ActionBuilder {
+ public:
+ // Creates a new ActionBuilder with a specific label and action runner.
+ ActionBuilder(const Label& label, const ActionRunner& runner);
-// Constructs a string appropriate for displaying program help, using
-// 'sub_actions' as a list of actions to include and 'usage_str' as a string
-// to prepend.
-std::string BuildHelpString(const std::vector<Action>& sub_actions,
- std::string usage_str);
+ // Add a new gflag to this builder. They are used when generating help.
+ ActionBuilder& AddGflag(const std::string& gflag);
+
+ // Creates an action using builder state.
+ std::unique_ptr<Action> Build();
+
+ private:
+ Label label_;
+
+ ActionRunner runner_;
+
+ std::vector<std::string> gflags_;
+};
+
+// A leaf node in the tree, representing a logical operation taken by the tool.
+class Action {
+ public:
+
+ // Returns the help for this action given its parent mode chain.
+ std::string BuildHelp(const std::vector<Mode*>& chain) const;
+
+ // Runs the operation represented by this action, given a parent mode chain
+ // and list of extra command line arguments.
+ Status Run(const std::vector<Mode*>& chain, std::deque<std::string> args) const;
+
+ const std::string& name() const { return label_.name; }
+
+ const std::string& description() const { return label_.description; }
+
+ private:
+ friend class ActionBuilder;
+
+ Action();
+
+ Label label_;
+
+ ActionRunner runner_;
+
+ // This action's gflags (if any).
+ std::vector<std::string> gflags_;
+};
// Removes one argument from 'remaining_args' and stores it in 'parsed_arg'.
//
@@ -98,22 +191,23 @@ Status ParseAndRemoveArg(const char* arg_name,
// Checks that 'args' is empty. If not, returns a bad status.
template <typename CONTAINER>
-Status CheckNoMoreArgs(const std::vector<Action>& chain,
+Status CheckNoMoreArgs(const std::vector<Mode*>& chain,
+ const Action* action,
const CONTAINER& args) {
if (args.empty()) {
return Status::OK();
}
DCHECK(!chain.empty());
- Action action = chain.back();
- return Status::InvalidArgument(strings::Substitute(
- "too many arguments\n$0", action.help(chain)));
+ return Status::InvalidArgument(
+ strings::Substitute("too many arguments: '$0'\n$1",
+ JoinStrings(args, " "), action->BuildHelp(chain)));
}
-// Returns the "fs" action node.
-Action BuildFsAction();
+// Returns a new "fs" mode node.
+std::unique_ptr<Mode> BuildFsMode();
-// Returns the "tablet" action node.
-Action BuildTabletAction();
+// Returns a new "tablet" mode node.
+std::unique_ptr<Mode> BuildTabletMode();
} // namespace tools
} // namespace kudu
http://git-wip-us.apache.org/repos/asf/kudu/blob/5ef37438/src/kudu/tools/tool_action_fs.cc
----------------------------------------------------------------------
diff --git a/src/kudu/tools/tool_action_fs.cc b/src/kudu/tools/tool_action_fs.cc
index b0b7c20..a77bc06 100644
--- a/src/kudu/tools/tool_action_fs.cc
+++ b/src/kudu/tools/tool_action_fs.cc
@@ -21,6 +21,7 @@
#include <deque>
#include <gflags/gflags.h>
#include <iostream>
+#include <memory>
#include <string>
#include "kudu/fs/fs_manager.h"
@@ -30,6 +31,7 @@ using std::cout;
using std::deque;
using std::endl;
using std::string;
+using std::unique_ptr;
using std::vector;
DEFINE_string(uuid, "",
@@ -40,8 +42,10 @@ namespace tools {
namespace {
-Status Format(const vector<Action>& chain, deque<string> args) {
- RETURN_NOT_OK(CheckNoMoreArgs(chain, args));
+Status Format(const vector<Mode*>& chain,
+ const Action* action,
+ deque<string> args) {
+ RETURN_NOT_OK(CheckNoMoreArgs(chain, action, args));
FsManager fs_manager(Env::Default(), FsManagerOpts());
boost::optional<string> uuid;
@@ -51,8 +55,10 @@ Status Format(const vector<Action>& chain, deque<string> args) {
return fs_manager.CreateInitialFileSystemLayout(uuid);
}
-Status PrintUuid(const vector<Action>& chain, deque<string> args) {
- RETURN_NOT_OK(CheckNoMoreArgs(chain, args));
+Status PrintUuid(const vector<Mode*>& chain,
+ const Action* action,
+ deque<string> args) {
+ RETURN_NOT_OK(CheckNoMoreArgs(chain, action, args));
FsManagerOpts opts;
opts.read_only = true;
@@ -64,30 +70,24 @@ Status PrintUuid(const vector<Action>& chain, deque<string> args) {
} // anonymous namespace
-Action BuildFsAction() {
- Action fs_format;
- fs_format.name = "format";
- fs_format.description = "Format a new Kudu filesystem";
- fs_format.help = &BuildLeafActionHelpString;
- fs_format.run = &Format;
- fs_format.gflags = { "fs_wal_dir", "fs_data_dirs", "uuid" };
-
- Action fs_print_uuid;
- fs_print_uuid.name = "print_uuid";
- fs_print_uuid.description = "Print the UUID of a Kudu filesystem";
- fs_print_uuid.help = &BuildLeafActionHelpString;
- fs_print_uuid.run = &PrintUuid;
- fs_print_uuid.gflags = { "fs_wal_dir", "fs_data_dirs" };
-
- Action fs;
- fs.name = "fs";
- fs.description = "Operate on a local Kudu filesystem";
- fs.help = &BuildNonLeafActionHelpString;
- fs.sub_actions = {
- fs_format,
- fs_print_uuid
- };
- return fs;
+unique_ptr<Mode> BuildFsMode() {
+ unique_ptr<Action> format = ActionBuilder(
+ { "format", "Format a new Kudu filesystem" }, &Format)
+ .AddGflag("fs_wal_dir")
+ .AddGflag("fs_data_dirs")
+ .AddGflag("uuid")
+ .Build();
+
+ unique_ptr<Action> print_uuid = ActionBuilder(
+ { "print_uuid", "Print the UUID of a Kudu filesystem" }, &PrintUuid)
+ .AddGflag("fs_wal_dir")
+ .AddGflag("fs_data_dirs")
+ .Build();
+
+ return ModeBuilder({ "fs", "Operate on a local Kudu filesystem" })
+ .AddAction(std::move(format))
+ .AddAction(std::move(print_uuid))
+ .Build();
}
} // namespace tools
http://git-wip-us.apache.org/repos/asf/kudu/blob/5ef37438/src/kudu/tools/tool_action_tablet.cc
----------------------------------------------------------------------
diff --git a/src/kudu/tools/tool_action_tablet.cc b/src/kudu/tools/tool_action_tablet.cc
index 1dbd253..a34ced7 100644
--- a/src/kudu/tools/tool_action_tablet.cc
+++ b/src/kudu/tools/tool_action_tablet.cc
@@ -95,11 +95,13 @@ Status ParsePeerString(const string& peer_str,
return Status::OK();
}
-Status PrintReplicaUuids(const vector<Action>& chain, deque<string> args) {
+Status PrintReplicaUuids(const vector<Mode*>& chain,
+ const Action* action,
+ deque<string> args) {
// Parse tablet ID argument.
string tablet_id;
RETURN_NOT_OK(ParseAndRemoveArg("tablet ID", &args, &tablet_id));
- RETURN_NOT_OK(CheckNoMoreArgs(chain, args));
+ RETURN_NOT_OK(CheckNoMoreArgs(chain, action, args));
FsManagerOpts opts;
opts.read_only = true;
@@ -116,7 +118,9 @@ Status PrintReplicaUuids(const vector<Action>& chain, deque<string> args) {
return Status::OK();
}
-Status RewriteRaftConfig(const vector<Action>& chain, deque<string> args) {
+Status RewriteRaftConfig(const vector<Mode*>& chain,
+ const Action* action,
+ deque<string> args) {
// Parse tablet ID argument.
string tablet_id;
RETURN_NOT_OK(ParseAndRemoveArg("tablet ID", &args, &tablet_id));
@@ -171,14 +175,16 @@ Status RewriteRaftConfig(const vector<Action>& chain, deque<string> args) {
return cmeta->Flush();
}
-Status Copy(const vector<Action>& chain, deque<string> args) {
+Status Copy(const vector<Mode*>& chain,
+ const Action* action,
+ deque<string> args) {
// Parse the tablet ID and source arguments.
string tablet_id;
RETURN_NOT_OK(ParseAndRemoveArg("tablet ID", &args, &tablet_id));
string rpc_address;
RETURN_NOT_OK(ParseAndRemoveArg("source RPC address of form hostname:port",
&args, &rpc_address));
- RETURN_NOT_OK(CheckNoMoreArgs(chain, args));
+ RETURN_NOT_OK(CheckNoMoreArgs(chain, action, args));
HostPort hp;
RETURN_NOT_OK(ParseHostPortString(rpc_address, &hp));
@@ -197,52 +203,40 @@ Status Copy(const vector<Action>& chain, deque<string> args) {
} // anonymous namespace
-Action BuildTabletAction() {
+unique_ptr<Mode> BuildTabletMode() {
// TODO: Need to include required arguments in the help for these actions.
- Action tablet_print_replica_uuids;
- tablet_print_replica_uuids.name = "print_replica_uuids";
- tablet_print_replica_uuids.description =
- "Print all replica UUIDs found in a tablet's Raft configuration";
- tablet_print_replica_uuids.help = &BuildLeafActionHelpString;
- tablet_print_replica_uuids.run = &PrintReplicaUuids;
- tablet_print_replica_uuids.gflags = { "fs_wal_dir", "fs_data_dirs" };
-
-
- Action tablet_rewrite_raft_config;
- tablet_rewrite_raft_config.name = "rewrite_raft_config";
- tablet_rewrite_raft_config.description =
- "Rewrite a replica's Raft configuration";
- tablet_rewrite_raft_config.help = &BuildLeafActionHelpString;
- tablet_rewrite_raft_config.run = &RewriteRaftConfig;
- tablet_rewrite_raft_config.gflags = { "fs_wal_dir", "fs_data_dirs" };
-
- Action tablet_cmeta;
- tablet_cmeta.name = "cmeta";
- tablet_cmeta.description =
- "Operate on a local Kudu tablet's consensus metadata file";
- tablet_cmeta.help = &BuildNonLeafActionHelpString;
- tablet_cmeta.sub_actions = {
- std::move(tablet_print_replica_uuids),
- std::move(tablet_rewrite_raft_config),
- };
-
- Action tablet_copy;
- tablet_copy.name = "copy";
- tablet_copy.description = "Copy a replica from a remote server";
- tablet_copy.help = &BuildLeafActionHelpString;
- tablet_copy.run = &Copy;
- tablet_copy.gflags = { "fs_wal_dir", "fs_data_dirs" };
-
- Action tablet;
- tablet.name = "tablet";
- tablet.description = "Operate on a local Kudu replica";
- tablet.help = &BuildNonLeafActionHelpString;
- tablet.sub_actions = {
- tablet_cmeta,
- tablet_copy
- };
- return tablet;
+ unique_ptr<Action> print_replica_uuids = ActionBuilder(
+ { "print_replica_uuids",
+ "Print all replica UUIDs found in a tablet's Raft configuration" },
+ &PrintReplicaUuids)
+ .AddGflag("fs_wal_dir")
+ .AddGflag("fs_data_dirs")
+ .Build();
+
+ unique_ptr<Action> rewrite_raft_config = ActionBuilder(
+ { "rewrite_raft_config", "Rewrite a replica's Raft configuration" },
+ &RewriteRaftConfig)
+ .AddGflag("fs_wal_dir")
+ .AddGflag("fs_data_dirs")
+ .Build();
+
+ unique_ptr<Mode> cmeta = ModeBuilder(
+ { "cmeta", "Operate on a local Kudu tablet's consensus metadata file" })
+ .AddAction(std::move(print_replica_uuids))
+ .AddAction(std::move(rewrite_raft_config))
+ .Build();
+
+ unique_ptr<Action> copy = ActionBuilder(
+ { "copy", "Copy a replica from a remote server" }, &Copy)
+ .AddGflag("fs_wal_dir")
+ .AddGflag("fs_data_dirs")
+ .Build();
+
+ return ModeBuilder({ "tablet", "Operate on a local Kudu replica" })
+ .AddMode(std::move(cmeta))
+ .AddAction(std::move(copy))
+ .Build();
}
} // namespace tools
http://git-wip-us.apache.org/repos/asf/kudu/blob/5ef37438/src/kudu/tools/tool_main.cc
----------------------------------------------------------------------
diff --git a/src/kudu/tools/tool_main.cc b/src/kudu/tools/tool_main.cc
index 5ffc4a7..96a74fa 100644
--- a/src/kudu/tools/tool_main.cc
+++ b/src/kudu/tools/tool_main.cc
@@ -19,6 +19,7 @@
#include <gflags/gflags.h>
#include <glog/logging.h>
#include <iostream>
+#include <memory>
#include <string>
#include <vector>
@@ -40,16 +41,17 @@ using std::cout;
using std::deque;
using std::endl;
using std::string;
+using std::unique_ptr;
using std::vector;
using strings::Substitute;
namespace kudu {
namespace tools {
-int DispatchCommand(const vector<Action>& chain, const deque<string>& args) {
- DCHECK(!chain.empty());
- Action action = chain.back();
- Status s = action.run(chain, args);
+int DispatchCommand(const vector<Mode*>& chain,
+ Action* action,
+ const deque<string>& args) {
+ Status s = action->Run(chain, args);
if (s.ok()) {
return 0;
} else {
@@ -58,63 +60,70 @@ int DispatchCommand(const vector<Action>& chain, const deque<string>& args) {
}
}
-int RunTool(const Action& root, int argc, char** argv, bool show_help) {
+int RunTool(int argc, char** argv, bool show_help) {
+ unique_ptr<Mode> root =
+ ModeBuilder({ argv[0], "" }) // root mode description isn't printed
+ .AddMode(BuildFsMode())
+ .AddMode(BuildTabletMode())
+ .Build();
+
// Initialize arg parsing state.
- vector<Action> chain = { root };
+ vector<Mode*> chain = { root.get() };
- // Parse the arguments, matching them up with actions.
+ // Parse the arguments, matching each to a mode or action.
for (int i = 1; i < argc; i++) {
- const Action* cur = &chain.back();
- const auto& sub_actions = cur->sub_actions;
- if (sub_actions.empty()) {
- // We've reached an invokable action.
- if (show_help) {
- cerr << cur->help(chain) << endl;
- return 1;
- } else {
- // Invoke it with whatever arguments remain.
- deque<string> remaining_args;
- for (int j = i; j < argc; j++) {
- remaining_args.push_back(argv[j]);
- }
- return DispatchCommand(chain, remaining_args);
+ Mode* cur = chain.back();
+ Mode* next_mode = nullptr;
+ Action* next_action = nullptr;
+
+ // Match argument with a mode.
+ for (const auto& m : cur->modes()) {
+ if (m->name() == argv[i]) {
+ next_mode = m.get();
+ break;
}
}
- // This action is not invokable. Interpret the next command line argument
- // as a subaction and continue parsing.
- const Action* next = nullptr;
- for (const auto& a : sub_actions) {
- if (a.name == argv[i]) {
- next = &a;
+ // Match argument with an action.
+ for (const auto& a : cur->actions()) {
+ if (a->name() == argv[i]) {
+ next_action = a.get();
break;
}
}
- if (next == nullptr) {
- // We couldn't find a subaction for the next argument. Raise an error.
- string msg = Substitute("$0 $1\n",
- BuildActionChainString(chain), argv[i]);
- msg += BuildHelpString(sub_actions, BuildUsageString(chain));
- Status s = Status::InvalidArgument(msg);
- cerr << s.ToString() << endl;
+ // If both matched, there's an error with the tree.
+ DCHECK(!next_mode || !next_action);
+
+ if (next_mode) {
+ // Add the mode and keep parsing.
+ chain.push_back(next_mode);
+ } else if (next_action) {
+ if (show_help) {
+ cerr << next_action->BuildHelp(chain) << endl;
+ return 1;
+ } else {
+ // Invoke the action with whatever arguments remain, skipping this one.
+ deque<string> remaining_args;
+ for (int j = i + 1; j < argc; j++) {
+ remaining_args.push_back(argv[j]);
+ }
+ return DispatchCommand(chain, next_action, remaining_args);
+ }
+ } else {
+ // Couldn't match the argument at all. Print the help.
+ Status s = Status::InvalidArgument(
+ Substitute("unknown command '$0'\n", argv[i]));
+ cerr << s.ToString() << cur->BuildHelp(chain) << endl;
return 1;
}
-
- // We're done parsing this argument. Loop and continue.
- chain.emplace_back(*next);
}
- // We made it to a subaction with no arguments left. Run the subaction if
- // possible, otherwise print its help.
- const Action* last = &chain.back();
- if (show_help || !last->run) {
- cerr << last->help(chain) << endl;
- return 1;
- } else {
- DCHECK(last->run);
- return DispatchCommand(chain, {});
- }
+ // Ran out of arguments before reaching an action. Print the last mode's help.
+ DCHECK(!chain.empty());
+ const Mode* last = chain.back();
+ cerr << last->BuildHelp(chain) << endl;
+ return 1;
}
} // namespace tools
@@ -145,21 +154,8 @@ static bool ParseCommandLineFlags(int* argc, char*** argv) {
}
int main(int argc, char** argv) {
- kudu::tools::Action root = {
- argv[0],
- "The root action", // doesn't matter, won't get printed
- nullptr,
- &kudu::tools::BuildNonLeafActionHelpString,
- {
- kudu::tools::BuildFsAction(),
- kudu::tools::BuildTabletAction()
- },
- {} // no gflags
- };
- string usage = root.help({ root });
- google::SetUsageMessage(usage);
bool show_help = ParseCommandLineFlags(&argc, &argv);
FLAGS_logtostderr = true;
kudu::InitGoogleLoggingSafe(argv[0]);
- return kudu::tools::RunTool(root, argc, argv, show_help);
+ return kudu::tools::RunTool(argc, argv, show_help);
}