/* * Copyright (c) 2018 MariaDB Corporation Ab * * Use of this software is governed by the Business Source License included * in the LICENSE.TXT file and at www.mariadb.com/bsl11. * * Change Date: 2023-11-12 * * On the date above, in accordance with the Business Source License, use * of this software will be governed by version 2 or later of the General * Public License. */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include namespace config { class Configuration; class Param; class Type; // An instance of Specification specifies what parameters a particular module expects // and of what type they are. class Specification { public: enum Kind { FILTER, MONITOR, ROUTER }; using ParamsByName = std::map; using const_iterator = ParamsByName::const_iterator; using value_type = ParamsByName::value_type; /** * Constructor * * @param zModule The the name of the module, e.g. "cachefilter". */ Specification(const char* zModule, Kind kind); ~Specification(); /** * @return What kind of specification. */ Kind kind() const; /** * @return The module name of this specification. */ const std::string& module() const; /** * Validate parameters * * @param params Parameters as found in the configuration file. * * @return True, if they represent valid parameters - all mandatory are present, * all present ones are of corrent type - for this configuration. */ bool validate(const MXS_CONFIG_PARAMETER& params) const; /** * Configure configuration * * @param configuration The configuration that should be configured. * @param params The parameters that should be used, will be validated. * * @return True if could be configured. */ bool configure(Configuration& configuration, const MXS_CONFIG_PARAMETER& params) const; /** * Find given parameter of the specification. * * @param name The name of the parameter. * * @return The corresponding parameter object or NULL if the name is not a * parameter of the specification. */ const Param* find_param(const std::string& name) const; /** * Document this specification. * * @param out The stream the documentation should be written to. * * @return @c out */ std::ostream& document(std::ostream& out) const; /** * Populate legacy parameter definition. * * @note Only for a transitionary period. * * @param module The module description to be populated with parameters. */ void populate(MXS_MODULE& module) const; /** * @return The number of parameters in the specification. */ size_t size() const; /** * @return Const iterator to first parameter. */ const_iterator cbegin() const { return m_params.cbegin(); } /** * @return Const iterator to one past last parameter. */ const_iterator cend() const { return m_params.cend(); } private: friend Param; void insert(Param* pParam); void remove(Param* pParam); private: std::string m_module; Kind m_kind; ParamsByName m_params; }; /** * A instance of Param specifies a parameter of a module, that is, its name, * type, default value and whether it is mandatory or optional. */ class Param { public: enum Kind { MANDATORY, OPTIONAL }; ~Param(); /** * @return The name of the parameter. */ const std::string& name() const; /** * @return The type of the parameter (human readable). */ virtual std::string type() const = 0; /** * @return The description of the parameter. */ const std::string& description() const; /** * Document the parameter. * * The documentation of a parameters consists of its name, its type, * whether it is mandatory or optional (default value documented in * that case), and its description. * * @return The documentation. */ std::string documentation() const; /** * @return The kind - mandatory or optional - of the parameter. */ Kind kind() const; /** * @return True, if the parameter is mandatory. */ bool is_mandatory() const; /** * @return True, if the parameter is optional. */ bool is_optional() const; /** * Synonym for @c is_optional. * * @return True, if the parameter has a default value. */ bool has_default_value() const; /** * @return Default value as string. * * @note Meaningful only if @c has_default_value returns true. */ virtual std::string default_to_string() const = 0; /** * Validate a string. * * @param value_as_string The string to validate. * * @return True, if @c value_as_string can be converted into a value of this type. */ virtual bool validate(const std::string& value_as_string, std::string* pMessage) const = 0; /** * Set setting value with value from configuration file. * * @param value The @c Type to configure. * @param value_as_string The string value to configure it with. * * @return True, if it could be configured, false otherwise. The * function will fail only if @c value_as_string is invalid. */ virtual bool set(Type& value, const std::string& value_as_string) const = 0; /** * Populate a legacy parameter specification with data. * * @param param The legacy parameter specification to be populated. */ virtual void populate(MXS_MODULE_PARAM& param) const; protected: Param(Specification* pSpecification, const char* zName, const char* zDescription, Kind kind, mxs_module_param_type legacy_type); private: Specification& m_specification; std::string m_name; std::string m_description; Kind m_kind; mxs_module_param_type m_legacy_type; }; /** * ParamBool */ class ParamBool : public Param { public: using value_type = bool; ParamBool(Specification* pSpecification, const char* zName, const char* zDescription) : ParamBool(pSpecification, zName, zDescription, Param::MANDATORY, value_type()) { } ParamBool(Specification* pSpecification, const char* zName, const char* zDescription, value_type default_value) : ParamBool(pSpecification, zName, zDescription, Param::OPTIONAL, default_value) { } std::string type() const override; std::string default_to_string() const override; bool validate(const std::string& value_as_string, std::string* pMessage) const override; bool set(Type& value, const std::string& value_as_string) const override; bool from_string(const std::string& value, value_type* pValue, std::string* pMessage = nullptr) const; std::string to_string(value_type value) const; private: ParamBool(Specification* pSpecification, const char* zName, const char* zDescription, Kind kind, value_type default_value) : Param(pSpecification, zName, zDescription, kind, MXS_MODULE_PARAM_BOOL) , m_default_value(default_value) { } private: value_type m_default_value; }; class ParamNumber : public Param { public: using value_type = int64_t; std::string default_to_string() const override; bool validate(const std::string& value_as_string, std::string* pMessage) const override; bool set(Type& value, const std::string& value_as_string) const override; bool from_string(const std::string& value, value_type* pValue, std::string* pMessage = nullptr) const; std::string to_string(value_type value) const; protected: ParamNumber(Specification* pSpecification, const char* zName, const char* zDescription, Kind kind, mxs_module_param_type legacy_type, value_type default_value, value_type min_value, value_type max_value) : Param(pSpecification, zName, zDescription, kind, legacy_type) , m_default_value(default_value) , m_min_value(min_value <= max_value ? min_value : max_value) , m_max_value(max_value) { mxb_assert(min_value <= max_value); } private: value_type m_default_value; value_type m_min_value; value_type m_max_value; }; /** * ParamCount */ class ParamCount : public ParamNumber { public: ParamCount(Specification* pSpecification, const char* zName, const char* zDescription) : ParamCount(pSpecification, zName, zDescription, Param::MANDATORY, value_type(), 0, std::numeric_limits::max()) { } ParamCount(Specification* pSpecification, const char* zName, const char* zDescription, value_type min_value, value_type max_value) : ParamCount(pSpecification, zName, zDescription, Param::MANDATORY, value_type(), min_value, max_value) { } ParamCount(Specification* pSpecification, const char* zName, const char* zDescription, value_type default_value) : ParamCount(pSpecification, zName, zDescription, Param::OPTIONAL, default_value, 0, std::numeric_limits::max()) { } ParamCount(Specification* pSpecification, const char* zName, const char* zDescription, value_type default_value, value_type min_value, value_type max_value) : ParamCount(pSpecification, zName, zDescription, Param::OPTIONAL, default_value, min_value, max_value) { } std::string type() const override; private: ParamCount(Specification* pSpecification, const char* zName, const char* zDescription, Kind kind, value_type default_value, value_type min_value, value_type max_value) : ParamNumber(pSpecification, zName, zDescription, kind, MXS_MODULE_PARAM_COUNT, default_value, min_value >= 0 ? min_value : 0, max_value <= std::numeric_limits::max() ? max_value : std::numeric_limits::max()) { mxb_assert(min_value >= 0); mxb_assert(max_value <= std::numeric_limits::max()); } }; using ParamNatural = ParamCount; /** * ParamInteger */ class ParamInteger : public ParamNumber { public: ParamInteger(Specification* pSpecification, const char* zName, const char* zDescription) : ParamInteger(pSpecification, zName, zDescription, Param::MANDATORY, value_type(), std::numeric_limits::min(), std::numeric_limits::max()) { } ParamInteger(Specification* pSpecification, const char* zName, const char* zDescription, value_type min_value, value_type max_value) : ParamInteger(pSpecification, zName, zDescription, Param::MANDATORY, value_type(), min_value, max_value) { } ParamInteger(Specification* pSpecification, const char* zName, const char* zDescription, value_type default_value) : ParamInteger(pSpecification, zName, zDescription, Param::OPTIONAL, default_value, std::numeric_limits::min(), std::numeric_limits::max()) { } ParamInteger(Specification* pSpecification, const char* zName, const char* zDescription, value_type default_value, value_type min_value, value_type max_value) : ParamInteger(pSpecification, zName, zDescription, Param::OPTIONAL, default_value, min_value, max_value) { } std::string type() const override; private: ParamInteger(Specification* pSpecification, const char* zName, const char* zDescription, Kind kind, value_type default_value, value_type min_value, value_type max_value) : ParamNumber(pSpecification, zName, zDescription, kind, MXS_MODULE_PARAM_INT, default_value, min_value >= std::numeric_limits::min() ? min_value : std::numeric_limits::min(), max_value <= std::numeric_limits::max() ? max_value : std::numeric_limits::max()) { mxb_assert(min_value >= std::numeric_limits::min()); mxb_assert(max_value <= std::numeric_limits::max()); } }; /** * ParamDuration */ template class ParamDuration : public Param { public: using value_type = T; ParamDuration(Specification* pSpecification, const char* zName, const char* zDescription, mxs::config::DurationInterpretation interpretation) : ParamDuration(pSpecification, zName, zDescription, Param::MANDATORY, interpretation, value_type()) { } ParamDuration(Specification* pSpecification, const char* zName, const char* zDescription, mxs::config::DurationInterpretation interpretation, value_type default_value) : ParamDuration(pSpecification, zName, zDescription, Param::OPTIONAL, interpretation, default_value) { } std::string type() const override; std::string default_to_string() const override; bool validate(const std::string& value_as_string, std::string* pMessage) const override; bool set(Type& value, const std::string& value_as_string) const override; bool from_string(const std::string& value, value_type* pValue, std::string* pMessage = nullptr) const; std::string to_string(const value_type& value) const; private: ParamDuration(Specification* pSpecification, const char* zName, const char* zDescription, Kind kind, mxs::config::DurationInterpretation interpretation, value_type default_value) : Param(pSpecification, zName, zDescription, kind, MXS_MODULE_PARAM_DURATION) , m_interpretation(interpretation) , m_default_value(default_value) { } private: mxs::config::DurationInterpretation m_interpretation; value_type m_default_value; }; /** * ParamEnum */ template class ParamEnum : public Param { public: using value_type = T; ParamEnum(Specification* pSpecification, const char* zName, const char* zDescription, const std::vector>& enumeration) : ParamEnum(pSpecification, zName, zDescription, Param::MANDATORY, enumeration, value_type()) { } ParamEnum(Specification* pSpecification, const char* zName, const char* zDescription, const std::vector>& enumeration, value_type default_value) : ParamEnum(pSpecification, zName, zDescription, Param::OPTIONAL, enumeration, default_value) { } std::string type() const override; std::string default_to_string() const override; bool validate(const std::string& value_as_string, std::string* pMessage) const override; bool set(Type& value, const std::string& value_as_string) const override; bool from_string(const std::string& value, value_type* pValue, std::string* pMessage = nullptr) const; std::string to_string(value_type value) const; void populate(MXS_MODULE_PARAM& param) const; private: ParamEnum(Specification* pSpecification, const char* zName, const char* zDescription, Kind kind, const std::vector>& enumeration, value_type default_value); private: std::vector> m_enumeration; value_type m_default_value; std::vector m_enum_values; }; /** * ParamPath */ class ParamPath : public Param { public: using value_type = std::string; enum Options { X = MXS_MODULE_OPT_PATH_X_OK, // Execute permission required. R = MXS_MODULE_OPT_PATH_R_OK, // Read permission required. W = MXS_MODULE_OPT_PATH_W_OK, // Write permission required. F = MXS_MODULE_OPT_PATH_F_OK, // File existence required. C = MXS_MODULE_OPT_PATH_CREAT // Create path if does not exist. }; const uint32_t MASK = X | R | W | F | C; ParamPath(Specification* pSpecification, const char* zName, const char* zDescription, uint32_t options) : ParamPath(pSpecification, zName, zDescription, Param::MANDATORY, options, value_type()) { } ParamPath(Specification* pSpecification, const char* zName, const char* zDescription, uint32_t options, value_type default_value) : ParamPath(pSpecification, zName, zDescription, Param::OPTIONAL, options, default_value) { } std::string type() const override; std::string default_to_string() const override; bool validate(const std::string& value_as_string, std::string* pMessage) const override; bool set(Type& value, const std::string& value_as_string) const override; bool from_string(const std::string& value, value_type* pValue, std::string* pMessage = nullptr) const; std::string to_string(const value_type& value) const; void populate(MXS_MODULE_PARAM& param) const; private: ParamPath(Specification* pSpecification, const char* zName, const char* zDescription, Kind kind, uint32_t options, value_type default_value) : Param(pSpecification, zName, zDescription, kind, MXS_MODULE_PARAM_PATH) , m_options(options) , m_default_value(default_value) { } private: uint32_t m_options; value_type m_default_value; }; /** * ParamServer */ class ParamServer : public Param { public: using value_type = SERVER*; ParamServer(Specification* pSpecification, const char* zName, const char* zDescription) : Param(pSpecification, zName, zDescription, Param::MANDATORY, MXS_MODULE_PARAM_SERVER) { } std::string type() const override; std::string default_to_string() const override; bool validate(const std::string& value_as_string, std::string* pMessage) const override; bool set(Type& value, const std::string& value_as_string) const override; bool from_string(const std::string& value, value_type* pValue, std::string* pMessage = nullptr) const; std::string to_string(value_type value) const; }; /** * ParamSize */ class ParamSize : public Param { public: using value_type = uint64_t; ParamSize(Specification* pSpecification, const char* zName, const char* zDescription) : ParamSize(pSpecification, zName, zDescription, Param::MANDATORY, value_type()) { } ParamSize(Specification* pSpecification, const char* zName, const char* zDescription, value_type default_value) : ParamSize(pSpecification, zName, zDescription, Param::OPTIONAL, default_value) { } std::string type() const override; std::string default_to_string() const override; bool validate(const std::string& value_as_string, std::string* pMessage) const override; bool set(Type& value, const std::string& value_as_string) const override; bool from_string(const std::string& value, value_type* pValue, std::string* pMessage = nullptr) const; std::string to_string(value_type value) const; private: ParamSize(Specification* pSpecification, const char* zName, const char* zDescription, Kind kind, value_type default_value) : Param(pSpecification, zName, zDescription, kind, MXS_MODULE_PARAM_SIZE) , m_default_value(default_value) { } private: value_type m_default_value; }; /** * ParamString */ class ParamString : public Param { public: using value_type = std::string; ParamString(Specification* pSpecification, const char* zName, const char* zDescription) : ParamString(pSpecification, zName, zDescription, Param::MANDATORY, value_type()) { } ParamString(Specification* pSpecification, const char* zName, const char* zDescription, value_type default_value) : ParamString(pSpecification, zName, zDescription, Param::OPTIONAL, default_value) { } std::string type() const override; std::string default_to_string() const override; bool validate(const std::string& value_as_string, std::string* pMessage) const override; bool set(Type& value, const std::string& value_as_string) const override; bool from_string(const std::string& value, value_type* pValue, std::string* pMessage = nullptr) const; std::string to_string(value_type value) const; private: ParamString(Specification* pSpecification, const char* zName, const char* zDescription, Kind kind, value_type default_value) : Param(pSpecification, zName, zDescription, kind, MXS_MODULE_PARAM_STRING) , m_default_value(default_value) { } private: value_type m_default_value; }; /** * ParamBitMask */ using ParamBitMask = ParamCount; /** * An instance of the class Configuration specifies the configuration of a particular * instance of a module. * * Walks hand in hand with Specification. */ class Configuration { public: using ValuesByName = std::map; using const_iterator = ValuesByName::const_iterator; using value_type = ValuesByName::value_type; /** * Constructor * * @param name The object (i.e. section name) of this configuration. * @param pSpecification The specification this instance is a configuration of. */ Configuration(const std::string& name, const Specification* pSpecification); /** * @return The The object (i.e. section name) of this configuration. */ const std::string& name() const; /** * @return The specification of this configuration. */ const Specification& specification() const; /** * @param name The name of the parameter to look up. * * @return The corresponding @c Value or NULL if @c name is unknown. */ Type* find_value(const std::string& name); const Type* find_value(const std::string& name) const; /** * Persist the configuration to a stream. * * @param out The stream to persist to. */ std::ostream& persist(std::ostream& out) const; /** * Called when configuration has initially been configured, to allow a * Configuration to check any interdependencies between values or to calculate * derived ones. * * @param params The parameters the configuration was configured with. * * @return True, if everything is ok. * * @note The default implementation returns true. */ virtual bool post_configure(const MXS_CONFIG_PARAMETER& params); /** * @return The number of values in the configuration. */ size_t size() const; /** * @return Const iterator to first parameter. */ const_iterator cbegin() const { return m_values.cbegin(); } /** * @return Const iterator to one past last parameter. */ const_iterator cend() const { return m_values.cend(); } private: friend Type; void insert(Type* pValue); void remove(Type* pValue, const std::string& name); private: std::string m_name; const Specification& m_specification; ValuesByName m_values; }; /** * Base-class of all configuration value types. * * In the description of this class, "value" should be read as * "an instance of this type". */ class Type { public: Type(const Type& rhs) = delete; Type& operator=(const Type&) = delete; ~Type(); /** * Get parameter describing this value. * * @return Param of the value. */ const Param& parameter() const; /** * Persist this value to a stream. It will be written as * * name=value * * where @c value will be formatted in the correct way. * * @param out The stream to write to. * * @return @c out. */ std::ostream& persist(std::ostream& out) const; /** * Convert this value into its string representation. * * @return The value as it should appear in a configuration file. */ virtual std::string to_string() const = 0; /** * Set value. * * @param value_as_string The new value expressed as a string. * * @return True, if the value could be set, false otherwise. */ bool set(const std::string& value_as_string); protected: Type(Configuration* pConfiguration, const Param* pParam); private: Configuration& m_configuration; const Param& m_param; const std::string m_name; }; /** * A concrete Value. Instantiated with a derived class and the * corresponding param type. */ template class ConcreteType : public Type { public: using value_type = typename ParamType::value_type; ConcreteType(const ConcreteType&) = delete; ConcreteType(Configuration* pConfiguration, const ParamType* pParam) : Type(pConfiguration, pParam) { } This& operator=(const value_type& value) { m_value = value; return static_cast(*this); } This& operator=(const ConcreteType& rhs) { // Only the value is copied, the parameter and the configuration // remains the same. m_value = rhs.m_value; return static_cast(*this); } value_type get() const { return m_value; } void set(const value_type& value) { m_value = value; } std::string to_string() const override { return static_cast(parameter()).to_string(m_value); } protected: value_type m_value; }; /** * Comparison operators: * * ConcreteType <-> ConcreteType */ template inline bool operator==(const ConcreteType& lhs, const ConcreteType& rhs) { return lhs.get() == rhs.get(); } template inline bool operator!=(const ConcreteType& lhs, const ConcreteType& rhs) { return lhs.get() != rhs.get(); } template inline bool operator<(const ConcreteType& lhs, const ConcreteType& rhs) { return lhs.get() < rhs.get(); } template inline bool operator>(const ConcreteType& lhs, const ConcreteType& rhs) { return lhs.get() > rhs.get(); } template inline bool operator<=(const ConcreteType& lhs, const ConcreteType& rhs) { return (lhs.get() < rhs.get()) || (lhs == rhs); } template inline bool operator>=(const ConcreteType& lhs, const ConcreteType& rhs) { return (lhs.get() > rhs.get()) || (lhs == rhs); } /** * Comparison operators: * * ConcreteType <-> ParamType::value_type */ template inline bool operator==(const ConcreteType& lhs, const typename ParamType::value_type& rhs) { return lhs.get() == rhs; } template inline bool operator!=(const ConcreteType& lhs, const typename ParamType::value_type& rhs) { return lhs.get() != rhs; } template inline bool operator<(const ConcreteType& lhs, const typename ParamType::value_type& rhs) { return lhs.get() < rhs; } template inline bool operator>(const ConcreteType& lhs, const typename ParamType::value_type& rhs) { return lhs.get() > rhs; } template inline bool operator<=(const ConcreteType& lhs, const typename ParamType::value_type& rhs) { return (lhs.get() < rhs) || (lhs.get() == rhs); } template inline bool operator>=(const ConcreteType& lhs, const typename ParamType::value_type& rhs) { return (lhs.get() > rhs) || (lhs.get() == rhs); } /** * Comparison operators: * * ParamType::value_type <-> ConcreteType */ template inline bool operator==(const typename ParamType::value_type& lhs, const ConcreteType& rhs) { return lhs == rhs.get(); } template inline bool operator!=(const typename ParamType::value_type& lhs, const ConcreteType& rhs) { return lhs != rhs.get(); } template inline bool operator<(const typename ParamType::value_type& lhs, const ConcreteType& rhs) { return lhs < rhs.get(); } template inline bool operator>(const typename ParamType::value_type& lhs, const ConcreteType& rhs) { return lhs > rhs.get(); } template inline bool operator<=(const typename ParamType::value_type& lhs, const ConcreteType& rhs) { return (lhs < rhs.get()) || (lhs == rhs.get()); } template inline bool operator>=(const typename ParamType::value_type& lhs, const ConcreteType& rhs) { return (lhs > rhs.get()) || (lhs == rhs.get()); } class Number : public ConcreteType { protected: using ConcreteType::operator =; Number(Configuration* pConfiguration, const ParamNumber* pParam) : ConcreteType(pConfiguration, pParam) { } }; /** * Count */ class Count : public Number { public: using Number::operator =; Count(Configuration* pConfiguration, const ParamCount* pParam) : Number(pConfiguration, pParam) { } }; /** * Integer */ class Integer : public Number { public: using Number::operator =; Integer(Configuration* pConfiguration, const ParamInteger* pParam) : Number(pConfiguration, pParam) { } }; /** * BitMask */ class BitMask : public Count { public: using Count::operator =; BitMask(Configuration* pConfiguration, const ParamCount* pParam) : Count(pConfiguration, pParam) { } bool is_set(value_type bit) const { return (m_value & bit) == bit; } }; /** * Bool */ class Bool : public ConcreteType { public: using ConcreteType::operator =; Bool(Configuration* pConfiguration, const ParamBool* pParam) : ConcreteType(pConfiguration, pParam) { } explicit operator bool() const { return m_value; } }; /** * Duration */ template class Duration : public ConcreteType, ParamDuration> { public: using ConcreteType, ParamDuration>::operator =; Duration(Configuration* pConfiguration, const ParamDuration* pParam) : ConcreteType, ParamDuration>(pConfiguration, pParam) { } typename T::rep count() const { return ConcreteType, ParamDuration>::m_value.count(); } }; /* * template * inline bool operator < (const Duration& lhs, const Duration& rhs) * { * return lhs.get() < rhs.get(); * } * * template * inline bool operator > (const Duration& lhs, const Duration& rhs) * { * return lhs.get() > rhs.get(); * } */ /** * Enum */ template class Enum : public ConcreteType, ParamEnum> { public: using ConcreteType, ParamEnum>::operator =; Enum(Configuration* pConfiguration, const ParamEnum* pParam) : ConcreteType, ParamEnum>(pConfiguration, pParam) { } }; /** * Path */ class Path : public ConcreteType { public: using ConcreteType::operator =; Path(Configuration* pConfiguration, const ParamPath* pParam) : ConcreteType(pConfiguration, pParam) { } const char* c_str() const { return m_value.c_str(); } bool empty() const { return m_value.empty(); } }; /** * Size */ class Size : public ConcreteType { public: using ConcreteType::operator =; Size(Configuration* pConfiguration, const ParamSize* pParam) : ConcreteType(pConfiguration, pParam) { } }; inline Size::value_type operator/(const Size& lhs, Size::value_type rhs) { return lhs.get() / rhs; } /** * Server */ class Server : public ConcreteType { public: using ConcreteType::operator =; Server(Configuration* pConfiguration, const ParamServer* pParam) : ConcreteType(pConfiguration, pParam) { } }; /** * String */ class String : public ConcreteType { public: using ConcreteType::operator =; String(Configuration* pConfiguration, const ParamString* pParam) : ConcreteType(pConfiguration, pParam) { } const char* c_str() const { return m_value.c_str(); } bool empty() const { return m_value.empty(); } }; /** * IMPLEMENTATION DETAILS */ struct DurationSuffix { static const char* of(const std::chrono::seconds&) { return "s"; } static const char* of(const std::chrono::milliseconds&) { return "ms"; } }; template std::string ParamDuration::type() const { return "duration"; } template std::string ParamDuration::default_to_string() const { return to_string(m_default_value); } template bool ParamDuration::validate(const std::string& value_as_string, std::string* pMessage) const { value_type value; return from_string(value_as_string, &value, pMessage); } template bool ParamDuration::set(Type& value, const std::string& value_as_string) const { mxb_assert(&value.parameter() == this); Duration& duration_value = static_cast&>(value); value_type x; bool valid = from_string(value_as_string, &x); if (valid) { duration_value.set(x); } return valid; } template bool ParamDuration::from_string(const std::string& value_as_string, value_type* pValue, std::string* pMessage) const { mxs::config::DurationUnit unit; std::chrono::milliseconds duration; bool valid = get_suffixed_duration(value_as_string.c_str(), m_interpretation, &duration, &unit); if (valid) { if (unit == mxs::config::DURATION_IN_DEFAULT) { if (pMessage) { *pMessage = "Specifying durations without a suffix denoting the unit has been deprecated: "; *pMessage += value_as_string; *pMessage += ". Use the suffixes 'h' (hour), 'm' (minute) 's' (second) or "; *pMessage += "'ms' (milliseconds)."; } } *pValue = std::chrono::duration_cast(duration); } else if (pMessage) { *pMessage = "Invalid duration: "; *pMessage += value_as_string; } return valid; } template std::string ParamDuration::to_string(const value_type& value) const { std::stringstream ss; ss << value.count() << DurationSuffix::of(value); return ss.str(); } template ParamEnum::ParamEnum(Specification* pSpecification, const char* zName, const char* zDescription, Kind kind, const std::vector>& enumeration, value_type default_value) : Param(pSpecification, zName, zDescription, kind, MXS_MODULE_PARAM_ENUM) , m_enumeration(enumeration) , m_default_value(default_value) { m_enum_values.reserve(m_enumeration.size() + 1); for (const auto& entry : enumeration) { MXS_ENUM_VALUE x {}; x.name = entry.second; x.enum_value = entry.first; m_enum_values.emplace_back(x); } MXS_ENUM_VALUE end {NULL}; m_enum_values.emplace_back(end); } template std::string ParamEnum::type() const { std::string s("enumeration:["); bool first = true; for (const auto& p : m_enumeration) { if (first) { first = false; } else { s += ", "; } s += p.second; } s += "]"; return s; } template std::string ParamEnum::default_to_string() const { return to_string(m_default_value); } template bool ParamEnum::validate(const std::string& value_as_string, std::string* pMessage) const { value_type value; return from_string(value_as_string, &value, pMessage); } template bool ParamEnum::set(Type& value, const std::string& value_as_string) const { mxb_assert(&value.parameter() == this); Enum& enum_value = static_cast&>(value); value_type x; bool valid = from_string(value_as_string, &x); if (valid) { enum_value.set(x); } return valid; } template bool ParamEnum::from_string(const std::string& value_as_string, value_type* pValue, std::string* pMessage) const { auto it = std::find_if(m_enumeration.begin(), m_enumeration.end(), [value_as_string](const std::pair& elem) { return value_as_string == elem.second; }); if (it != m_enumeration.end()) { *pValue = it->first; } else if (pMessage) { std::string s; for (size_t i = 0; i < m_enumeration.size(); ++i) { s += "'"; s += m_enumeration[i].second; s += "'"; if (i == m_enumeration.size() - 2) { s += " and "; } else if (i != m_enumeration.size() - 1) { s += ", "; } } *pMessage = "Invalid enumeration value: "; *pMessage += value_as_string; *pMessage += ", valid values are: "; *pMessage += s; *pMessage += "."; } return it != m_enumeration.end(); } template std::string ParamEnum::to_string(value_type value) const { auto it = std::find_if(m_enumeration.begin(), m_enumeration.end(), [value](const std::pair& entry) { return entry.first == value; }); return it != m_enumeration.end() ? it->second : "unknown"; } template void ParamEnum::populate(MXS_MODULE_PARAM& param) const { Param::populate(param); param.accepted_values = &m_enum_values[0]; } }