IssueIDs #666 and #672.
Change-Id: I001966ab3ba44c59b0186456a6467322b3b3d26d
//! Returns the name of the module.
virtual const char *name() const = 0;
+ //! Returns a one-line description of the module.
+ virtual const char *shortDescription() const = 0;
/*! \brief
* Runs the module with the given arguments.
typedef std::map<std::string, CommandLineModulePointer> ModuleMap;
//! Prints usage message to stderr.
- void printUsage() const;
+ void printUsage(bool bModuleList) const;
+ //! Prints the list of modules to stderr.
+ void printModuleList() const;
/*! \brief
* Maps module names to module objects.
ModuleMap modules_;
};
-void CommandLineModuleManager::Impl::printUsage() const
+void CommandLineModuleManager::Impl::printUsage(bool bModuleList) const
+{
+ const char *program = ShortProgram();
+ fprintf(stderr, "Usage: %s <command> [<args>]\n\n", program);
+ if (bModuleList)
+ {
+ printModuleList();
+ }
+ else
+ {
+ fprintf(stderr, "See '%s help' for list of commands.\n", program);
+ }
+}
+
+void CommandLineModuleManager::Impl::printModuleList() const
+{
+ int maxNameLength = 0;
+ ModuleMap::const_iterator module;
+ for (module = modules_.begin(); module != modules_.end(); ++module)
+ {
+ int nameLength = static_cast<int>(module->first.length());
+ if (nameLength > maxNameLength)
+ {
+ maxNameLength = nameLength;
+ }
+ }
+ fprintf(stderr, "The following commands are available:\n");
+ for (module = modules_.begin(); module != modules_.end(); ++module)
+ {
+ const char *name = module->first.c_str();
+ const char *description = module->second->shortDescription();
+ fprintf(stderr, " %*s %s\n", -maxNameLength, name, description);
+ }
+}
+
+
+/********************************************************************
+ * CommandLineHelpModule
+ */
+
+namespace internal
+{
+
+/*! \internal \brief
+ * Command-line module for producing help.
+ *
+ * This module implements the 'help' subcommand that is automatically added by
+ * CommandLineModuleManager.
+ *
+ * \ingroup module_commandline
+ */
+class CommandLineHelpModule : public CommandLineModuleInterface
+{
+ public:
+ /*! \brief
+ * Creates a help module for the given module manager.
+ *
+ * \param[in] manager Manager for which this module provides help.
+ *
+ * Does not throw.
+ */
+ explicit CommandLineHelpModule(const CommandLineModuleManager &manager);
+
+ virtual const char *name() const { return "help"; }
+ virtual const char *shortDescription() const
+ {
+ return "Print help information";
+ }
+
+ virtual int run(int argc, char *argv[]);
+
+ private:
+ const CommandLineModuleManager &manager_;
+
+ GMX_DISALLOW_COPY_AND_ASSIGN(CommandLineHelpModule);
+};
+
+CommandLineHelpModule::CommandLineHelpModule(const CommandLineModuleManager &manager)
+ : manager_(manager)
{
- fprintf(stderr, "Usage: %s <command> [<args>]\n",
- ShortProgram());
}
+int CommandLineHelpModule::run(int argc, char *argv[])
+{
+ manager_.impl_->printUsage(true);
+ return 0;
+}
+
+} // namespace internal
+
+
/********************************************************************
* CommandLineModuleManager
*/
CommandLineModuleManager::CommandLineModuleManager()
: impl_(new Impl)
{
+ addModule(CommandLineModulePointer(new internal::CommandLineHelpModule(*this)));
}
CommandLineModuleManager::~CommandLineModuleManager()
if (argc < 2)
{
fprintf(stderr, "\n");
- impl_->printUsage();
+ impl_->printUsage(false);
return 2;
}
// TODO: Accept unambiguous prefixes?
if (module == impl_->modules_.end())
{
fprintf(stderr, "\n");
- fprintf(stderr, "Unknown command: %s\n", argv[1]);
- impl_->printUsage();
+ fprintf(stderr, "Unknown command: '%s'\n", argv[1]);
+ impl_->printUsage(true);
return 2;
}
return module->second->run(argc - 1, argv + 1);
typedef gmx_unique_ptr<CommandLineModuleInterface>::type
CommandLineModulePointer;
+namespace internal
+{
+class CommandLineHelpModule;
+}
+
/*! \brief
* Implements a wrapper command-line interface for multiple modules.
*
class Impl;
PrivateImplPointer<Impl> impl_;
+
+ /*! \brief
+ * Needed to access information about registered modules etc.
+ */
+ friend class internal::CommandLineHelpModule;
};
} // namespace gmx
class MockModule : public gmx::CommandLineModuleInterface
{
public:
- //! Creates a mock module with the given name.
- explicit MockModule(const char *name);
+ //! Creates a mock module with the given name and description.
+ MockModule(const char *name, const char *description);
virtual const char *name() const { return name_; }
+ virtual const char *shortDescription() const { return descr_; }
MOCK_METHOD2(run, int(int argc, char *argv[]));
private:
const char *name_;
+ const char *descr_;
};
-MockModule::MockModule(const char *name)
- : name_(name)
+MockModule::MockModule(const char *name, const char *description)
+ : name_(name), descr_(description)
{
}
class CommandLineModuleManagerTest : public ::testing::Test
{
public:
- MockModule &addModule(const char *name);
+ MockModule &addModule(const char *name, const char *description);
gmx::CommandLineModuleManager manager_;
};
MockModule &
-CommandLineModuleManagerTest::addModule(const char *name)
+CommandLineModuleManagerTest::addModule(const char *name, const char *description)
{
- MockModule *module = new MockModule(name);
+ MockModule *module = new MockModule(name, description);
manager_.addModule(gmx::CommandLineModulePointer(module));
return *module;
}
"test", "module", "-flag", "yes"
};
gmx::test::CommandLine args(cmdline);
- MockModule &mod1 = addModule("module");
- addModule("other");
+ MockModule &mod1 = addModule("module", "First module");
+ addModule("other", "Second module");
using ::testing::_;
using ::testing::Args;
using ::testing::ElementsAreArray;
{
public:
virtual const char *name() const = 0;
+ virtual const char *shortDescription() const = 0;
virtual int run(int argc, char *argv[]);
* \tparam Module Trajectory analysis module to wrap.
*
* \p Module should be default-constructible, derive from
- * TrajectoryAnalysisModule, and have a static public member
- * \c "const char name[]".
+ * TrajectoryAnalysisModule, and have a static public members
+ * \c "const char name[]" and \c "const char shortDescription[]".
*
* \ingroup module_trajectoryanalysis
*/
{
return Module::name;
}
+ virtual const char *shortDescription() const
+ {
+ return Module::shortDescription;
+ }
virtual TrajectoryAnalysisModulePointer createModule()
{
return TrajectoryAnalysisModulePointer(new Module);
{
const char Angle::name[] = "angle";
+const char Angle::shortDescription[] =
+ "Calculate angles";
Angle::Angle()
- : _options(name, "Angle calculation"),
+ : _options(name, shortDescription),
_sel1info(NULL), _sel2info(NULL),
_bSplit1(false), _bSplit2(false), _bMulti(false), _bAll(false),
_bDumpDist(false), _natoms1(0), _natoms2(0), _vt0(NULL)
{
public:
static const char name[];
+ static const char shortDescription[];
Angle();
virtual ~Angle();
{
const char Distance::name[] = "distance";
+const char Distance::shortDescription[] =
+ "Calculate distances";
Distance::Distance()
- : _options(name, "Distance calculation"), _avem(new AnalysisDataAverageModule())
+ : _options(name, shortDescription), _avem(new AnalysisDataAverageModule())
{
}
{
public:
static const char name[];
+ static const char shortDescription[];
Distance();
virtual ~Distance();
*/
const char Select::name[] = "select";
+const char Select::shortDescription[] =
+ "Print general information about selections";
Select::Select()
- : _options(name, "Selection information"),
+ : _options(name, shortDescription),
_bDump(false), _bTotNorm(false), _bFracNorm(false), _bResInd(false),
_top(NULL)
{
{
public:
static const char name[];
+ static const char shortDescription[];
Select();
virtual ~Select();