mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-04 19:25:51 +00:00
Per XLS-0095, we are taking steps to rename ripple(d) to xrpl(d). This change specifically removes all copyright notices referencing Ripple, XRPLF, and certain affiliated contributors upon mutual agreement, so the notice in the LICENSE.md file applies throughout. Copyright notices referencing external contributions remain as-is. Duplicate verbiage is also removed.
386 lines
9.0 KiB
C++
386 lines
9.0 KiB
C++
#ifndef XRPL_BASICS_BASICCONFIG_H_INCLUDED
|
|
#define XRPL_BASICS_BASICCONFIG_H_INCLUDED
|
|
|
|
#include <xrpl/basics/contract.h>
|
|
|
|
#include <boost/beast/core/string.hpp>
|
|
#include <boost/lexical_cast.hpp>
|
|
|
|
#include <algorithm>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
namespace ripple {
|
|
|
|
using IniFileSections =
|
|
std::unordered_map<std::string, std::vector<std::string>>;
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
/** Holds a collection of configuration values.
|
|
A configuration file contains zero or more sections.
|
|
*/
|
|
class Section
|
|
{
|
|
private:
|
|
std::string name_;
|
|
std::unordered_map<std::string, std::string> lookup_;
|
|
std::vector<std::string> lines_;
|
|
std::vector<std::string> values_;
|
|
bool had_trailing_comments_ = false;
|
|
|
|
using const_iterator = decltype(lookup_)::const_iterator;
|
|
|
|
public:
|
|
/** Create an empty section. */
|
|
explicit Section(std::string const& name = "");
|
|
|
|
/** Returns the name of this section. */
|
|
std::string const&
|
|
name() const
|
|
{
|
|
return name_;
|
|
}
|
|
|
|
/** Returns all the lines in the section.
|
|
This includes everything.
|
|
*/
|
|
std::vector<std::string> const&
|
|
lines() const
|
|
{
|
|
return lines_;
|
|
}
|
|
|
|
/** Returns all the values in the section.
|
|
Values are non-empty lines which are not key/value pairs.
|
|
*/
|
|
std::vector<std::string> const&
|
|
values() const
|
|
{
|
|
return values_;
|
|
}
|
|
|
|
/**
|
|
* Set the legacy value for this section.
|
|
*/
|
|
void
|
|
legacy(std::string value)
|
|
{
|
|
if (lines_.empty())
|
|
lines_.emplace_back(std::move(value));
|
|
else
|
|
lines_[0] = std::move(value);
|
|
}
|
|
|
|
/**
|
|
* Get the legacy value for this section.
|
|
*
|
|
* @return The retrieved value. A section with an empty legacy value returns
|
|
an empty string.
|
|
*/
|
|
std::string
|
|
legacy() const
|
|
{
|
|
if (lines_.empty())
|
|
return "";
|
|
if (lines_.size() > 1)
|
|
Throw<std::runtime_error>(
|
|
"A legacy value must have exactly one line. Section: " + name_);
|
|
return lines_[0];
|
|
}
|
|
|
|
/** Set a key/value pair.
|
|
The previous value is discarded.
|
|
*/
|
|
void
|
|
set(std::string const& key, std::string const& value);
|
|
|
|
/** Append a set of lines to this section.
|
|
Lines containing key/value pairs are added to the map,
|
|
else they are added to the values list. Everything is
|
|
added to the lines list.
|
|
*/
|
|
void
|
|
append(std::vector<std::string> const& lines);
|
|
|
|
/** Append a line to this section. */
|
|
void
|
|
append(std::string const& line)
|
|
{
|
|
append(std::vector<std::string>{line});
|
|
}
|
|
|
|
/** Returns `true` if a key with the given name exists. */
|
|
bool
|
|
exists(std::string const& name) const;
|
|
|
|
template <class T = std::string>
|
|
std::optional<T>
|
|
get(std::string const& name) const
|
|
{
|
|
auto const iter = lookup_.find(name);
|
|
if (iter == lookup_.end())
|
|
return std::nullopt;
|
|
return boost::lexical_cast<T>(iter->second);
|
|
}
|
|
|
|
/// Returns a value if present, else another value.
|
|
template <class T>
|
|
T
|
|
value_or(std::string const& name, T const& other) const
|
|
{
|
|
auto const v = get<T>(name);
|
|
return v.has_value() ? *v : other;
|
|
}
|
|
|
|
// indicates if trailing comments were seen
|
|
// during the appending of any lines/values
|
|
bool
|
|
had_trailing_comments() const
|
|
{
|
|
return had_trailing_comments_;
|
|
}
|
|
|
|
friend std::ostream&
|
|
operator<<(std::ostream&, Section const& section);
|
|
|
|
// Returns `true` if there are no key/value pairs.
|
|
bool
|
|
empty() const
|
|
{
|
|
return lookup_.empty();
|
|
}
|
|
|
|
// Returns the number of key/value pairs.
|
|
std::size_t
|
|
size() const
|
|
{
|
|
return lookup_.size();
|
|
}
|
|
|
|
// For iteration of key/value pairs.
|
|
const_iterator
|
|
begin() const
|
|
{
|
|
return lookup_.cbegin();
|
|
}
|
|
|
|
// For iteration of key/value pairs.
|
|
const_iterator
|
|
cbegin() const
|
|
{
|
|
return lookup_.cbegin();
|
|
}
|
|
|
|
// For iteration of key/value pairs.
|
|
const_iterator
|
|
end() const
|
|
{
|
|
return lookup_.cend();
|
|
}
|
|
|
|
// For iteration of key/value pairs.
|
|
const_iterator
|
|
cend() const
|
|
{
|
|
return lookup_.cend();
|
|
}
|
|
};
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
/** Holds unparsed configuration information.
|
|
The raw data sections are processed with intermediate parsers specific
|
|
to each module instead of being all parsed in a central location.
|
|
*/
|
|
class BasicConfig
|
|
{
|
|
private:
|
|
std::unordered_map<std::string, Section> map_;
|
|
|
|
public:
|
|
/** Returns `true` if a section with the given name exists. */
|
|
bool
|
|
exists(std::string const& name) const;
|
|
|
|
/** Returns the section with the given name.
|
|
If the section does not exist, an empty section is returned.
|
|
*/
|
|
/** @{ */
|
|
Section&
|
|
section(std::string const& name);
|
|
|
|
Section const&
|
|
section(std::string const& name) const;
|
|
|
|
Section const&
|
|
operator[](std::string const& name) const
|
|
{
|
|
return section(name);
|
|
}
|
|
|
|
Section&
|
|
operator[](std::string const& name)
|
|
{
|
|
return section(name);
|
|
}
|
|
/** @} */
|
|
|
|
/** Overwrite a key/value pair with a command line argument
|
|
If the section does not exist it is created.
|
|
The previous value, if any, is overwritten.
|
|
*/
|
|
void
|
|
overwrite(
|
|
std::string const& section,
|
|
std::string const& key,
|
|
std::string const& value);
|
|
|
|
/** Remove all the key/value pairs from the section.
|
|
*/
|
|
void
|
|
deprecatedClearSection(std::string const& section);
|
|
|
|
/**
|
|
* Set a value that is not a key/value pair.
|
|
*
|
|
* The value is stored as the section's first value and may be retrieved
|
|
* through section::legacy.
|
|
*
|
|
* @param section Name of the section to modify.
|
|
* @param value Contents of the legacy value.
|
|
*/
|
|
void
|
|
legacy(std::string const& section, std::string value);
|
|
|
|
/**
|
|
* Get the legacy value of a section. A section with a
|
|
* single-line value may be retrieved as a legacy value.
|
|
*
|
|
* @param sectionName Retrieve the contents of this section's
|
|
* legacy value.
|
|
* @return Contents of the legacy value.
|
|
*/
|
|
std::string
|
|
legacy(std::string const& sectionName) const;
|
|
|
|
friend std::ostream&
|
|
operator<<(std::ostream& ss, BasicConfig const& c);
|
|
|
|
// indicates if trailing comments were seen
|
|
// in any loaded Sections
|
|
bool
|
|
had_trailing_comments() const
|
|
{
|
|
return std::any_of(map_.cbegin(), map_.cend(), [](auto s) {
|
|
return s.second.had_trailing_comments();
|
|
});
|
|
}
|
|
|
|
protected:
|
|
void
|
|
build(IniFileSections const& ifs);
|
|
};
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
/** Set a value from a configuration Section
|
|
If the named value is not found or doesn't parse as a T,
|
|
the variable is unchanged.
|
|
@return `true` if value was set.
|
|
*/
|
|
template <class T>
|
|
bool
|
|
set(T& target, std::string const& name, Section const& section)
|
|
{
|
|
bool found_and_valid = false;
|
|
try
|
|
{
|
|
auto const val = section.get<T>(name);
|
|
if ((found_and_valid = val.has_value()))
|
|
target = *val;
|
|
}
|
|
catch (boost::bad_lexical_cast&)
|
|
{
|
|
}
|
|
return found_and_valid;
|
|
}
|
|
|
|
/** Set a value from a configuration Section
|
|
If the named value is not found or doesn't cast to T,
|
|
the variable is assigned the default.
|
|
@return `true` if the named value was found and is valid.
|
|
*/
|
|
template <class T>
|
|
bool
|
|
set(T& target,
|
|
T const& defaultValue,
|
|
std::string const& name,
|
|
Section const& section)
|
|
{
|
|
bool found_and_valid = set<T>(target, name, section);
|
|
if (!found_and_valid)
|
|
target = defaultValue;
|
|
return found_and_valid;
|
|
}
|
|
|
|
/** Retrieve a key/value pair from a section.
|
|
@return The value string converted to T if it exists
|
|
and can be parsed, or else defaultValue.
|
|
*/
|
|
// NOTE This routine might be more clumsy than the previous two
|
|
template <class T = std::string>
|
|
T
|
|
get(Section const& section,
|
|
std::string const& name,
|
|
T const& defaultValue = T{})
|
|
{
|
|
try
|
|
{
|
|
return section.value_or<T>(name, defaultValue);
|
|
}
|
|
catch (boost::bad_lexical_cast&)
|
|
{
|
|
}
|
|
return defaultValue;
|
|
}
|
|
|
|
inline std::string
|
|
get(Section const& section, std::string const& name, char const* defaultValue)
|
|
{
|
|
try
|
|
{
|
|
auto const val = section.get(name);
|
|
if (val.has_value())
|
|
return *val;
|
|
}
|
|
catch (boost::bad_lexical_cast&)
|
|
{
|
|
}
|
|
return defaultValue;
|
|
}
|
|
|
|
template <class T>
|
|
bool
|
|
get_if_exists(Section const& section, std::string const& name, T& v)
|
|
{
|
|
return set<T>(v, name, section);
|
|
}
|
|
|
|
template <>
|
|
inline bool
|
|
get_if_exists<bool>(Section const& section, std::string const& name, bool& v)
|
|
{
|
|
int intVal = 0;
|
|
auto stat = get_if_exists(section, name, intVal);
|
|
if (stat)
|
|
v = bool(intVal);
|
|
return stat;
|
|
}
|
|
|
|
} // namespace ripple
|
|
|
|
#endif
|