diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/util/options_parser/environment.cpp | 22 | ||||
-rw-r--r-- | src/mongo/util/options_parser/environment.h | 6 | ||||
-rw-r--r-- | src/mongo/util/options_parser/environment_test.cpp | 18 | ||||
-rw-r--r-- | src/mongo/util/options_parser/option_description.cpp | 21 | ||||
-rw-r--r-- | src/mongo/util/options_parser/option_description.h | 41 | ||||
-rw-r--r-- | src/mongo/util/options_parser/option_section.cpp | 20 | ||||
-rw-r--r-- | src/mongo/util/options_parser/option_section.h | 7 | ||||
-rw-r--r-- | src/mongo/util/options_parser/options_parser.cpp | 26 | ||||
-rw-r--r-- | src/mongo/util/options_parser/options_parser_test.cpp | 70 |
9 files changed, 211 insertions, 20 deletions
diff --git a/src/mongo/util/options_parser/environment.cpp b/src/mongo/util/options_parser/environment.cpp index cc4ad2a71aa..bbf23d54168 100644 --- a/src/mongo/util/options_parser/environment.cpp +++ b/src/mongo/util/options_parser/environment.cpp @@ -28,12 +28,12 @@ namespace optionenvironment { // Environment implementation - Status Environment::addKeyConstraint(KeyConstraint* c) { - keyConstraints.push_back(boost::shared_ptr<KeyConstraint>(c)); + Status Environment::addKeyConstraint(KeyConstraint* keyConstraint) { + keyConstraints.push_back(keyConstraint); return Status::OK(); } - Status Environment::addConstraint(Constraint* c) { - constraints.push_back(boost::shared_ptr<Constraint>(c)); + Status Environment::addConstraint(Constraint* constraint) { + constraints.push_back(constraint); return Status::OK(); } @@ -110,7 +110,7 @@ namespace optionenvironment { // 2. Add values to be added std::map <Key, Value> add_values = add_environment.values; - for(std::map<Key, Value>::const_iterator iterator = add_values.begin(); + for (std::map<Key, Value>::const_iterator iterator = add_values.begin(); iterator != add_values.end(); iterator++) { values[iterator->first] = iterator->second; } @@ -134,20 +134,20 @@ namespace optionenvironment { Status Environment::validate() { // 1. Iterate and check all KeyConstraints - typedef std::vector<boost::shared_ptr<KeyConstraint> >::iterator it_keyConstraint; - for(it_keyConstraint iterator = keyConstraints.begin(); + typedef std::vector<KeyConstraint*>::iterator it_keyConstraint; + for (it_keyConstraint iterator = keyConstraints.begin(); iterator != keyConstraints.end(); iterator++) { - Status ret = (*(*iterator).get())(*this); + Status ret = (**iterator)(*this); if (!ret.isOK()) { return ret; } } // 2. Iterate and check all Constraints - typedef std::vector<boost::shared_ptr<Constraint> >::iterator it_constraint; - for(it_constraint iterator = constraints.begin(); + typedef std::vector<Constraint*>::iterator it_constraint; + for (it_constraint iterator = constraints.begin(); iterator != constraints.end(); iterator++) { - Status ret = (*(*iterator).get())(*this); + Status ret = (**iterator)(*this); if (!ret.isOK()) { return ret; } diff --git a/src/mongo/util/options_parser/environment.h b/src/mongo/util/options_parser/environment.h index f7c8929814d..0e096ef40ad 100644 --- a/src/mongo/util/options_parser/environment.h +++ b/src/mongo/util/options_parser/environment.h @@ -107,7 +107,7 @@ namespace optionenvironment { * * It is an error to call these functions after "validate" has been called * - * WARNING: These take ownership of the pointer passed in + * NOTE: These DO NOT take ownership of the pointer passed in */ Status addKeyConstraint(KeyConstraint* keyConstraint); Status addConstraint(Constraint* constraint); @@ -197,8 +197,8 @@ namespace optionenvironment { void dump(); protected: - std::vector<boost::shared_ptr<Constraint> > constraints; - std::vector<boost::shared_ptr<KeyConstraint> > keyConstraints; + std::vector<Constraint*> constraints; + std::vector<KeyConstraint*> keyConstraints; std::map <Key, Value> values; std::map <Key, Value> default_values; bool valid; diff --git a/src/mongo/util/options_parser/environment_test.cpp b/src/mongo/util/options_parser/environment_test.cpp index 959d1d08f7b..c24d4a81423 100644 --- a/src/mongo/util/options_parser/environment_test.cpp +++ b/src/mongo/util/options_parser/environment_test.cpp @@ -33,7 +33,8 @@ namespace { TEST(Environment, Immutable) { moe::Environment environment; - environment.addKeyConstraint(new moe::ImmutableKeyConstraint(moe::Key("port"))); + moe::ImmutableKeyConstraint immutableKeyConstraint(moe::Key("port")); + environment.addKeyConstraint(&immutableKeyConstraint); ASSERT_OK(environment.set(moe::Key("port"), moe::Value(5))); ASSERT_OK(environment.validate()); ASSERT_NOT_OK(environment.set(moe::Key("port"), moe::Value(0))); @@ -41,29 +42,34 @@ namespace { TEST(Environment, OutOfRange) { moe::Environment environment; - environment.addKeyConstraint(new moe::NumericKeyConstraint(moe::Key("port"), 1000, 65535)); + moe::NumericKeyConstraint numericKeyConstraint(moe::Key("port"), 1000, 65535); + environment.addKeyConstraint(&numericKeyConstraint); ASSERT_OK(environment.validate()); ASSERT_NOT_OK(environment.set(moe::Key("port"), moe::Value(0))); } TEST(Environment, NonNumericRangeConstraint) { moe::Environment environment; - environment.addKeyConstraint(new moe::NumericKeyConstraint(moe::Key("port"), 1000, 65535)); + moe::NumericKeyConstraint numericKeyConstraint(moe::Key("port"), 1000, 65535); + environment.addKeyConstraint(&numericKeyConstraint); ASSERT_OK(environment.validate()); ASSERT_NOT_OK(environment.set(moe::Key("port"), moe::Value("string"))); } TEST(Environment, BadType) { moe::Environment environment; - environment.addKeyConstraint(new moe::TypeKeyConstraint<int>(moe::Key("port"))); + moe::TypeKeyConstraint<int> typeKeyConstraintInt(moe::Key("port")); + environment.addKeyConstraint(&typeKeyConstraintInt); ASSERT_OK(environment.set(moe::Key("port"), moe::Value("string"))); ASSERT_NOT_OK(environment.validate()); } TEST(Environment, AllowNumeric) { moe::Environment environment; - environment.addKeyConstraint(new moe::TypeKeyConstraint<long>(moe::Key("port"))); - environment.addKeyConstraint(new moe::TypeKeyConstraint<int>(moe::Key("port"))); + moe::TypeKeyConstraint<long> typeKeyConstraintLong(moe::Key("port")); + environment.addKeyConstraint(&typeKeyConstraintLong); + moe::TypeKeyConstraint<int> typeKeyConstraintInt(moe::Key("port")); + environment.addKeyConstraint(&typeKeyConstraintInt); ASSERT_OK(environment.set(moe::Key("port"), moe::Value(1))); ASSERT_OK(environment.validate()); } diff --git a/src/mongo/util/options_parser/option_description.cpp b/src/mongo/util/options_parser/option_description.cpp index 1a439c99c5b..28e7f7a8f95 100644 --- a/src/mongo/util/options_parser/option_description.cpp +++ b/src/mongo/util/options_parser/option_description.cpp @@ -196,5 +196,26 @@ namespace optionenvironment { return *this; } + OptionDescription& OptionDescription::addConstraint(Constraint* c) { + _constraints.push_back(boost::shared_ptr<Constraint>(c)); + return *this; + } + + OptionDescription& OptionDescription::validRange(long min, long max) { + if (_type != Double && + _type != Int && + _type != Long && + _type != UnsignedLongLong && + _type != Unsigned) { + StringBuilder sb; + sb << "Could not register option \"" << _dottedName << "\": " + << "only options registered as a numeric type can have a valid range, " + << "but option has type: " << _type; + throw DBException(sb.str(), ErrorCodes::InternalError); + } + + return addConstraint(new NumericKeyConstraint(_dottedName, min, max)); + } + } // 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 cee9a0f2d2c..20b3fc98409 100644 --- a/src/mongo/util/options_parser/option_description.h +++ b/src/mongo/util/options_parser/option_description.h @@ -15,9 +15,11 @@ #pragma once +#include <boost/shared_ptr.hpp> #include <iostream> #include "mongo/base/status.h" +#include "mongo/util/options_parser/constraints.h" #include "mongo/util/options_parser/value.h" namespace mongo { @@ -79,6 +81,13 @@ namespace optionenvironment { * more details on the chaining interface. */ + /** + * Parsing Attributes. + * + * The functions below specify various attributes of our option that are relevant for + * parsing. + */ + /* * Make this option hidden so it does not appear in command line help */ @@ -140,6 +149,28 @@ namespace optionenvironment { */ OptionDescription& positional(int start, int end); + /** + * Validation Constraints. + * + * The functions below specify constraints that must be met in order for this option to be + * valid. These do not get checked during parsing, but will be added to the result + * Environment so that they will get checked when the Environment is validated. + */ + + /** + * Specifies the range allowed for this option. Only allowed for options with numeric type. + */ + OptionDescription& validRange(long min, long max); + + /** + * Adds a constraint for this option. During parsing, this Constraint will be added to the + * result Environment, ensuring that it will get checked when the environment is validated. + * See the documentation on the Constraint and Environment classes for more details. + * + * WARNING: This function takes ownership of the Constraint pointer that is passed in. + */ + OptionDescription& addConstraint(Constraint* c); + 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) @@ -153,6 +184,16 @@ namespace optionenvironment { // command line, json config, and ini config) int _positionalStart; // The starting position if this is a positional option. -1 otherwise. int _positionalEnd; // The ending position if this is a positional option. -1 if unlimited. + + // TODO(sverch): We have to use pointers to keep track of the Constrants because we rely on + // inheritance to make Constraints work. We have to use shared_ptrs because the + // OptionDescription is sometimes copied and because it is stored in a std::list in the + // OptionSection. We should think about a better solution for the ownership semantics of + // these classes. Note that the Environment (the storage for results of option parsing) has + // to know about the constraints for all the options, which is another factor to consider + // when thinking about ownership. + std::vector<boost::shared_ptr<Constraint> > _constraints; // Constraints that must be met + // for this option to be valid }; } // namespace optionenvironment diff --git a/src/mongo/util/options_parser/option_section.cpp b/src/mongo/util/options_parser/option_section.cpp index 26853f30f63..08879b297e5 100644 --- a/src/mongo/util/options_parser/option_section.cpp +++ b/src/mongo/util/options_parser/option_section.cpp @@ -508,6 +508,26 @@ namespace optionenvironment { return Status::OK(); } + Status OptionSection::getConstraints( + std::vector<boost::shared_ptr<Constraint > >* constraints) const { + + std::list<OptionDescription>::const_iterator oditerator; + for (oditerator = _options.begin(); oditerator != _options.end(); oditerator++) { + std::vector<boost::shared_ptr<Constraint> >::const_iterator citerator; + for (citerator = oditerator->_constraints.begin(); + citerator != oditerator->_constraints.end(); citerator++) { + constraints->push_back(*citerator); + } + } + + std::list<OptionSection>::const_iterator ositerator; + for (ositerator = _subSections.begin(); ositerator != _subSections.end(); ositerator++) { + ositerator->getConstraints(constraints); + } + + return Status::OK(); + } + std::string OptionSection::positionalHelpString(const std::string& execName) const { po::positional_options_description boostPositionalOptions; diff --git a/src/mongo/util/options_parser/option_section.h b/src/mongo/util/options_parser/option_section.h index 9535bf52741..0da5fba3a90 100644 --- a/src/mongo/util/options_parser/option_section.h +++ b/src/mongo/util/options_parser/option_section.h @@ -17,6 +17,7 @@ #include "mongo/util/options_parser/option_description.h" #include <boost/program_options.hpp> +#include <boost/shared_ptr.hpp> #include <iostream> #include <list> @@ -123,6 +124,12 @@ namespace optionenvironment { */ Status getDefaults(std::map<Key, Value>* values) const; + /** + * Populates the given vector with all the constraints for all options in this section and + * sub sections. + */ + Status getConstraints(std::vector<boost::shared_ptr<Constraint > >* constraints) const; + std::string positionalHelpString(const std::string& execName) const; std::string helpString() const; diff --git a/src/mongo/util/options_parser/options_parser.cpp b/src/mongo/util/options_parser/options_parser.cpp index 3725d7a7ffc..8873d19b409 100644 --- a/src/mongo/util/options_parser/options_parser.cpp +++ b/src/mongo/util/options_parser/options_parser.cpp @@ -418,6 +418,26 @@ namespace optionenvironment { return Status::OK(); } + /** + * For all options that have constraints, add those constraints to our environment so that + * they run when the environment gets validated. + */ + Status addConstraints(const OptionSection& options, Environment* dest) { + std::vector<boost::shared_ptr<Constraint> > constraints_vector; + Status ret = options.getConstraints(&constraints_vector); + if (!ret.isOK()) { + return ret; + } + + std::vector<boost::shared_ptr<Constraint> >::const_iterator citerator; + for (citerator = constraints_vector.begin(); + citerator != constraints_vector.end(); citerator++) { + dest->addConstraint(citerator->get()); + } + + return Status::OK(); + } + } // namespace /** @@ -783,6 +803,12 @@ namespace optionenvironment { return ret; } + // Add the constraints from our options to the result environment + ret = addConstraints(options, environment); + if (!ret.isOK()) { + return ret; + } + return Status::OK(); } diff --git a/src/mongo/util/options_parser/options_parser_test.cpp b/src/mongo/util/options_parser/options_parser_test.cpp index 7b1a69c0bc6..6d6f0f78330 100644 --- a/src/mongo/util/options_parser/options_parser_test.cpp +++ b/src/mongo/util/options_parser/options_parser_test.cpp @@ -18,6 +18,7 @@ #include "mongo/bson/util/builder.h" #include "mongo/unittest/unittest.h" +#include "mongo/util/options_parser/constraints.h" #include "mongo/util/options_parser/environment.h" #include "mongo/util/options_parser/option_description.h" #include "mongo/util/options_parser/option_section.h" @@ -214,6 +215,19 @@ namespace { } } + TEST(Registration, NumericRangeConstraint) { + moe::OptionSection testOpts; + try { + std::vector<std::string> defaultVal; + defaultVal.push_back("default"); + testOpts.addOptionChaining("port", "port", moe::String, "Port") + .validRange(1000, 65535); + FAIL("Was able to register non numeric option with constraint on range"); + } + catch (::mongo::DBException &e) { + } + } + TEST(Parsing, Good) { moe::OptionsParser parser; moe::Environment environment; @@ -2483,4 +2497,60 @@ namespace { ASSERT_OK(value.get(¶meter)); ASSERT_EQUALS(parameter, "allowed"); } + + TEST(Constraints, NumericRangeConstraint) { + OptionsParserTester parser; + moe::Environment environment; + moe::Value value; + std::vector<std::string> argv; + std::map<std::string, std::string> env_map; + int port; + + moe::OptionSection testOpts; + testOpts.addOptionChaining("config", "config", moe::String, "Config file to parse"); + testOpts.addOptionChaining("port", "port", moe::Int, "Port") + .validRange(1000, 65535); + + environment = moe::Environment(); + argv.clear(); + argv.push_back("binaryname"); + argv.push_back("--port"); + argv.push_back("999"); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_NOT_OK(environment.validate());; + + environment = moe::Environment(); + argv.clear(); + argv.push_back("binaryname"); + argv.push_back("--port"); + argv.push_back("65536"); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_NOT_OK(environment.validate());; + + environment = moe::Environment(); + argv.clear(); + argv.push_back("binaryname"); + argv.push_back("--port"); + argv.push_back("65535"); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.validate());; + ASSERT_OK(environment.get(moe::Key("port"), &value)); + ASSERT_OK(value.get(&port)); + ASSERT_EQUALS(port, 65535); + + environment = moe::Environment(); + argv.clear(); + argv.push_back("binaryname"); + argv.push_back("--port"); + argv.push_back("1000"); + + ASSERT_OK(parser.run(testOpts, argv, env_map, &environment)); + ASSERT_OK(environment.validate());; + ASSERT_OK(environment.get(moe::Key("port"), &value)); + ASSERT_OK(value.get(&port)); + ASSERT_EQUALS(port, 1000); + } } // unnamed namespace |