diff options
author | Shaun Verch <shaun.verch@10gen.com> | 2013-10-17 22:23:45 -0400 |
---|---|---|
committer | Shaun Verch <shaun.verch@10gen.com> | 2013-10-23 19:41:30 -0400 |
commit | 107978292c125ebb6674ad57548f1a4dc8f977d1 (patch) | |
tree | 3354909dcab4a6dfdd1013ea461bd948b54b19f7 /src/mongo/util | |
parent | e7089550acfde9507dcae677e03000e8b2dad3b4 (diff) | |
download | mongo-107978292c125ebb6674ad57548f1a4dc8f977d1.tar.gz |
SERVER-11144 Chaining interface for option registration
Diffstat (limited to 'src/mongo/util')
-rw-r--r-- | src/mongo/util/options_parser/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/util/options_parser/option_description.cpp | 39 | ||||
-rw-r--r-- | src/mongo/util/options_parser/option_description.h | 10 | ||||
-rw-r--r-- | src/mongo/util/options_parser/option_section.cpp | 40 | ||||
-rw-r--r-- | src/mongo/util/options_parser/option_section.h | 30 | ||||
-rw-r--r-- | src/mongo/util/options_parser/options_parser_test.cpp | 218 |
6 files changed, 322 insertions, 16 deletions
diff --git a/src/mongo/util/options_parser/SConscript b/src/mongo/util/options_parser/SConscript index 2390c19d6eb..0ca160f82a1 100644 --- a/src/mongo/util/options_parser/SConscript +++ b/src/mongo/util/options_parser/SConscript @@ -6,6 +6,7 @@ env.StaticLibrary('options_parser', ['environment.cpp', 'value.cpp', 'constraints.cpp', 'option_section.cpp', + 'option_description.cpp', 'options_parser.cpp', 'startup_option_init.cpp', 'startup_options.cpp', diff --git a/src/mongo/util/options_parser/option_description.cpp b/src/mongo/util/options_parser/option_description.cpp new file mode 100644 index 00000000000..fe052c9f716 --- /dev/null +++ b/src/mongo/util/options_parser/option_description.cpp @@ -0,0 +1,39 @@ +/* Copyright 2013 10gen Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mongo/util/options_parser/option_description.h" + +namespace mongo { +namespace optionenvironment { + + OptionDescription& OptionDescription::hidden() { + _isVisible = false; + return *this; + } + OptionDescription& OptionDescription::setDefault(Value defaultValue) { + _default = defaultValue; + return *this; + } + OptionDescription& OptionDescription::setImplicit(Value implicitValue) { + _implicit = implicitValue; + return *this; + } + OptionDescription& OptionDescription::composing() { + _isComposing = true; + return *this; + } + +} // namespace optionenvironment +} // namespace mongo diff --git a/src/mongo/util/options_parser/option_description.h b/src/mongo/util/options_parser/option_description.h index 6f3320e1e79..22fe12c0d74 100644 --- a/src/mongo/util/options_parser/option_description.h +++ b/src/mongo/util/options_parser/option_description.h @@ -62,6 +62,16 @@ namespace optionenvironment { _implicit(implicitValue), _isComposing(isComposing) { } + /* + * The following functions are part of the chaining interface for option registration. See + * comments below for what each of these attributes mean, and the OptionSection class for + * more details on the chaining interface. + */ + OptionDescription& hidden(); + OptionDescription& setDefault(Value defaultValue); + OptionDescription& setImplicit(Value implicitValue); + OptionDescription& composing(); + std::string _dottedName; // Used for JSON config and in Environment std::string _singleName; // Used for boost command line and INI OptionType _type; // Storage type of the argument value, or switch type (bool) diff --git a/src/mongo/util/options_parser/option_section.cpp b/src/mongo/util/options_parser/option_section.cpp index 79d20a8a5e4..1fef8c21b54 100644 --- a/src/mongo/util/options_parser/option_section.cpp +++ b/src/mongo/util/options_parser/option_section.cpp @@ -18,6 +18,7 @@ #include <sstream> #include "mongo/bson/util/builder.h" +#include "mongo/util/assert_util.h" #include "mongo/util/options_parser/value.h" namespace mongo { @@ -99,7 +100,7 @@ namespace optionenvironment { Status OptionSection::addOption(const OptionDescription& option) { // Verify that neither the single name nor the dotted name for this option conflicts with // the names for any options we have already registered - std::vector<OptionDescription>::const_iterator oditerator; + std::list<OptionDescription>::const_iterator oditerator; for (oditerator = _options.begin(); oditerator != _options.end(); oditerator++) { if (option._dottedName == oditerator->_dottedName) { StringBuilder sb; @@ -159,10 +160,23 @@ namespace optionenvironment { return Status::OK(); } + OptionDescription& OptionSection::addOptionChaining(const std::string& dottedName, + const std::string& singleName, + const OptionType type, + const std::string& description) { + OptionDescription option(dottedName, singleName, type, description); + Status ret = addOption(option); + if (!ret.isOK()) { + // TODO: Determine if this is the exception we want to throw + throw DBException(ret.reason(), ret.code()); + } + return _options.back(); + } + Status OptionSection::addPositionalOption(const PositionalOptionDescription& positionalOption) { // Verify that the name for this positional option does not conflict with the name for any // positional option we have already registered - std::vector<PositionalOptionDescription>::const_iterator poditerator; + std::list<PositionalOptionDescription>::const_iterator poditerator; for (poditerator = _positionalOptions.begin(); poditerator != _positionalOptions.end(); poditerator++) { if (positionalOption._name == poditerator->_name) { @@ -177,7 +191,7 @@ namespace optionenvironment { // positional options that we also want to be visible command line flags // // TODO: More robust way to do this. This only works if we register the flag first - std::vector<OptionDescription>::const_iterator oditerator; + std::list<OptionDescription>::const_iterator oditerator; for (oditerator = _options.begin(); oditerator != _options.end(); oditerator++) { if (positionalOption._name == oditerator->_dottedName) { _positionalOptions.push_back(positionalOption); @@ -465,7 +479,7 @@ namespace optionenvironment { bool visibleOnly, bool includeDefaults) const { - std::vector<OptionDescription>::const_iterator oditerator; + std::list<OptionDescription>::const_iterator oditerator; for (oditerator = _options.begin(); oditerator != _options.end(); oditerator++) { if (!visibleOnly || (oditerator->_isVisible)) { std::auto_ptr<po::value_semantic> boostType; @@ -485,7 +499,7 @@ namespace optionenvironment { } } - std::vector<OptionSection>::const_iterator ositerator; + std::list<OptionSection>::const_iterator ositerator; for (ositerator = _subSections.begin(); ositerator != _subSections.end(); ositerator++) { po::options_description subGroup = ositerator->_name.empty() ? po::options_description() @@ -500,7 +514,7 @@ namespace optionenvironment { Status OptionSection::getBoostPositionalOptions( po::positional_options_description* boostPositionalOptions) const { - std::vector<PositionalOptionDescription>::const_iterator poditerator; + std::list<PositionalOptionDescription>::const_iterator poditerator; for (poditerator = _positionalOptions.begin(); poditerator != _positionalOptions.end(); poditerator++) { boostPositionalOptions->add(poditerator->_name.c_str(), poditerator->_count); @@ -514,12 +528,12 @@ namespace optionenvironment { Status OptionSection::getAllOptions(std::vector<OptionDescription>* options) const { - std::vector<OptionDescription>::const_iterator oditerator; + std::list<OptionDescription>::const_iterator oditerator; for (oditerator = _options.begin(); oditerator != _options.end(); oditerator++) { options->push_back(*oditerator); } - std::vector<OptionSection>::const_iterator ositerator; + std::list<OptionSection>::const_iterator ositerator; for (ositerator = _subSections.begin(); ositerator != _subSections.end(); ositerator++) { ositerator->getAllOptions(options); } @@ -529,14 +543,14 @@ namespace optionenvironment { Status OptionSection::getDefaults(std::map<Key, Value>* values) const { - std::vector<OptionDescription>::const_iterator oditerator; + std::list<OptionDescription>::const_iterator oditerator; for (oditerator = _options.begin(); oditerator != _options.end(); oditerator++) { if (!oditerator->_default.isEmpty()) { (*values)[oditerator->_dottedName] = oditerator->_default; } } - std::vector<OptionSection>::const_iterator ositerator; + std::list<OptionSection>::const_iterator ositerator; for (ositerator = _subSections.begin(); ositerator != _subSections.end(); ositerator++) { ositerator->getDefaults(values); } @@ -602,7 +616,7 @@ namespace optionenvironment { /* Debugging */ void OptionSection::dump() const { - std::vector<PositionalOptionDescription>::const_iterator poditerator; + std::list<PositionalOptionDescription>::const_iterator poditerator; for (poditerator = _positionalOptions.begin(); poditerator != _positionalOptions.end(); poditerator++) { std::cout << " _name: " << poditerator->_name @@ -610,7 +624,7 @@ namespace optionenvironment { << " _count: " << poditerator->_count << std::endl; } - std::vector<OptionDescription>::const_iterator oditerator; + std::list<OptionDescription>::const_iterator oditerator; for (oditerator = _options.begin(); oditerator != _options.end(); oditerator++) { std::cout << " _dottedName: " << oditerator->_dottedName << " _singleName: " << oditerator->_singleName @@ -619,7 +633,7 @@ namespace optionenvironment { << " _isVisible: " << oditerator->_isVisible << std::endl; } - std::vector<OptionSection>::const_iterator ositerator; + std::list<OptionSection>::const_iterator ositerator; for (ositerator = _subSections.begin(); ositerator != _subSections.end(); ositerator++) { std::cout << "Section Name: " << ositerator->_name << std::endl; ositerator->dump(); diff --git a/src/mongo/util/options_parser/option_section.h b/src/mongo/util/options_parser/option_section.h index fd115e4d55d..f729bc74b1b 100644 --- a/src/mongo/util/options_parser/option_section.h +++ b/src/mongo/util/options_parser/option_section.h @@ -18,6 +18,7 @@ #include <boost/program_options.hpp> #include <iostream> +#include <list> #include "mongo/base/status.h" @@ -78,6 +79,29 @@ namespace optionenvironment { */ Status addOption(const OptionDescription& option); /** + * Add an option to this section, but returns a reference to an OptionDescription to allow + * for chaining. + * + * Example: + * + * options.addOptionChaining("option", "option", moe::String, "Chaining Registration") + * .hidden().setDefault(moe::Value("default")) + * .setImplicit(moe::Value("implicit")).composing(); + * + * This creates a hidden option that is composing and has default and implicit values. See + * the OptionDescription class for details on these attributes. + * + * throws DBException on errors, such as attempting to register an option with the same name + * as another option, registering a composing option that does not have the type + * StringVector, or registering an option with a default value that does not match its type. + * All of these cases represent programming errors and should not happen during normal + * operation. + */ + OptionDescription& addOptionChaining(const std::string& dottedName, + const std::string& singleName, + const OptionType type, + const std::string& description); + /** * Add a positional option to this section. Also adds a normal hidden option with the same * name as the PositionalOptionDescription because that is the mechanism boost program * options uses. Unfortunately this means that positional options can also be accessed by @@ -111,9 +135,9 @@ namespace optionenvironment { private: std::string _name; - std::vector<OptionSection> _subSections; - std::vector<OptionDescription> _options; - std::vector<PositionalOptionDescription> _positionalOptions; + std::list<OptionSection> _subSections; + std::list<OptionDescription> _options; + std::list<PositionalOptionDescription> _positionalOptions; }; } // namespace optionenvironment diff --git a/src/mongo/util/options_parser/options_parser_test.cpp b/src/mongo/util/options_parser/options_parser_test.cpp index 9c1abff4353..e8a028485c8 100644 --- a/src/mongo/util/options_parser/options_parser_test.cpp +++ b/src/mongo/util/options_parser/options_parser_test.cpp @@ -1626,4 +1626,222 @@ namespace { ASSERT_EQUALS(host, "localhost"); } + TEST(ChainingInterface, GoodReference) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + // This test is to make sure our reference stays good even after we add more options. This + // would not be true if we were using a std::vector in our option section which may need to + // be moved and resized. + moe::OptionDescription& optionRef = testOpts.addOptionChaining("ref", "ref", moe::String, + "Save this Reference"); + int i; + for (i = 0; i < 100; i++) { + ::mongo::StringBuilder sb; + sb << "filler" << i; + testOpts.addOptionChaining(sb.str(), sb.str(), moe::String, "Filler Option"); + } + moe::Value defaultVal("default"); + moe::Value implicitVal("implicit"); + optionRef.hidden().setDefault(defaultVal); + optionRef.setImplicit(implicitVal).composing(); + + std::vector<moe::OptionDescription> options_vector; + ASSERT_OK(testOpts.getAllOptions(&options_vector)); + + bool foundRef = false; + for(std::vector<moe::OptionDescription>::const_iterator iterator = options_vector.begin(); + iterator != options_vector.end(); iterator++) { + + if (iterator->_dottedName == "ref") { + ASSERT_EQUALS(iterator->_singleName, "ref"); + ASSERT_EQUALS(iterator->_type, moe::String); + ASSERT_EQUALS(iterator->_description, "Save this Reference"); + ASSERT_EQUALS(iterator->_isVisible, false); + ASSERT_TRUE(iterator->_default.equal(defaultVal)); + ASSERT_TRUE(iterator->_implicit.equal(implicitVal)); + ASSERT_EQUALS(iterator->_isComposing, true); + foundRef = true; + } + } + if (!foundRef) { + FAIL("Could not find \"ref\" options that we registered"); + } + } + + TEST(ChainingInterface, Basic) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("basic", + "basic", + moe::String, + "Default Option"); + + std::vector<moe::OptionDescription> options_vector; + ASSERT_OK(testOpts.getAllOptions(&options_vector)); + + for(std::vector<moe::OptionDescription>::const_iterator iterator = options_vector.begin(); + iterator != options_vector.end(); iterator++) { + + if (iterator->_dottedName == "basic") { + ASSERT_EQUALS(iterator->_singleName, "basic"); + ASSERT_EQUALS(iterator->_type, moe::String); + ASSERT_EQUALS(iterator->_description, "Default Option"); + ASSERT_EQUALS(iterator->_isVisible, true); + ASSERT_TRUE(iterator->_default.isEmpty()); + ASSERT_TRUE(iterator->_implicit.isEmpty()); + ASSERT_EQUALS(iterator->_isComposing, false); + } + else { + ::mongo::StringBuilder sb; + sb << "Found extra option: " << iterator->_dottedName << + " which we did not register"; + FAIL(sb.str()); + } + } + } + + TEST(ChainingInterface, Hidden) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("hidden", + "hidden", + moe::String, + "Hidden Option").hidden(); + + std::vector<moe::OptionDescription> options_vector; + ASSERT_OK(testOpts.getAllOptions(&options_vector)); + + for(std::vector<moe::OptionDescription>::const_iterator iterator = options_vector.begin(); + iterator != options_vector.end(); iterator++) { + + if (iterator->_dottedName == "hidden") { + ASSERT_EQUALS(iterator->_singleName, "hidden"); + ASSERT_EQUALS(iterator->_type, moe::String); + ASSERT_EQUALS(iterator->_description, "Hidden Option"); + ASSERT_EQUALS(iterator->_isVisible, false); + ASSERT_TRUE(iterator->_default.isEmpty()); + ASSERT_TRUE(iterator->_implicit.isEmpty()); + ASSERT_EQUALS(iterator->_isComposing, false); + } + else { + ::mongo::StringBuilder sb; + sb << "Found extra option: " << iterator->_dottedName << + " which we did not register"; + FAIL(sb.str()); + } + } + } + + TEST(ChainingInterface, DefaultValue) { + OptionsParserTester parser; + moe::Environment environment; + + moe::Value defaultVal("default"); + + moe::OptionSection testOpts; + testOpts.addOptionChaining("default", + "default", + moe::String, + "Option With Default Value").setDefault(defaultVal); + + std::vector<moe::OptionDescription> options_vector; + ASSERT_OK(testOpts.getAllOptions(&options_vector)); + + for(std::vector<moe::OptionDescription>::const_iterator iterator = options_vector.begin(); + iterator != options_vector.end(); iterator++) { + + if (iterator->_dottedName == "default") { + ASSERT_EQUALS(iterator->_singleName, "default"); + ASSERT_EQUALS(iterator->_type, moe::String); + ASSERT_EQUALS(iterator->_description, "Option With Default Value"); + ASSERT_EQUALS(iterator->_isVisible, true); + ASSERT_TRUE(iterator->_default.equal(defaultVal)); + ASSERT_TRUE(iterator->_implicit.isEmpty()); + ASSERT_EQUALS(iterator->_isComposing, false); + } + else { + ::mongo::StringBuilder sb; + sb << "Found extra option: " << iterator->_dottedName << + " which we did not register"; + FAIL(sb.str()); + } + } + } + + TEST(ChainingInterface, ImplicitValue) { + OptionsParserTester parser; + moe::Environment environment; + + moe::Value implicitVal("implicit"); + + moe::OptionSection testOpts; + testOpts.addOptionChaining("implicit", + "implicit", + moe::String, + "Option With Implicit Value").setImplicit(implicitVal); + + std::vector<moe::OptionDescription> options_vector; + ASSERT_OK(testOpts.getAllOptions(&options_vector)); + + for(std::vector<moe::OptionDescription>::const_iterator iterator = options_vector.begin(); + iterator != options_vector.end(); iterator++) { + + if (iterator->_dottedName == "implicit") { + ASSERT_EQUALS(iterator->_singleName, "implicit"); + ASSERT_EQUALS(iterator->_type, moe::String); + ASSERT_EQUALS(iterator->_description, "Option With Implicit Value"); + ASSERT_EQUALS(iterator->_isVisible, true); + ASSERT_TRUE(iterator->_default.isEmpty()); + ASSERT_TRUE(iterator->_implicit.equal(implicitVal)); + ASSERT_EQUALS(iterator->_isComposing, false); + } + else { + ::mongo::StringBuilder sb; + sb << "Found extra option: " << iterator->_dottedName << + " which we did not register"; + FAIL(sb.str()); + } + } + } + + TEST(ChainingInterface, Composing) { + OptionsParserTester parser; + moe::Environment environment; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("setParameter", + "setParameter", + moe::StringVector, + "Multiple Values").composing(); + + std::vector<moe::OptionDescription> options_vector; + ASSERT_OK(testOpts.getAllOptions(&options_vector)); + + for(std::vector<moe::OptionDescription>::const_iterator iterator = options_vector.begin(); + iterator != options_vector.end(); iterator++) { + + if (iterator->_dottedName == "setParameter") { + ASSERT_EQUALS(iterator->_singleName, "setParameter"); + ASSERT_EQUALS(iterator->_type, moe::StringVector); + ASSERT_EQUALS(iterator->_description, "Multiple Values"); + ASSERT_EQUALS(iterator->_isVisible, true); + ASSERT_TRUE(iterator->_default.isEmpty()); + ASSERT_TRUE(iterator->_implicit.isEmpty()); + ASSERT_EQUALS(iterator->_isComposing, true); + } + else { + ::mongo::StringBuilder sb; + sb << "Found extra option: " << iterator->_dottedName << + " which we did not register"; + FAIL(sb.str()); + } + } + } + } // unnamed namespace |