Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F13115508
D8011.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
6 KB
Subscribers
None
D8011.diff
View Options
diff --git a/src/test/settings_tests.cpp b/src/test/settings_tests.cpp
--- a/src/test/settings_tests.cpp
+++ b/src/test/settings_tests.cpp
@@ -7,6 +7,7 @@
#include <test/util/setup_common.h>
#include <test/util/str.h>
#include <util/strencodings.h>
+#include <util/system.h>
#include <univalue.h>
@@ -14,8 +15,94 @@
#include <vector>
+inline bool operator==(const util::SettingsValue &a,
+ const util::SettingsValue &b) {
+ return a.write() == b.write();
+}
+
+inline std::ostream &operator<<(std::ostream &os,
+ const util::SettingsValue &value) {
+ os << value.write();
+ return os;
+}
+
+inline std::ostream &
+operator<<(std::ostream &os,
+ const std::pair<std::string, util::SettingsValue> &kv) {
+ util::SettingsValue out(util::SettingsValue::VOBJ);
+ out.__pushKV(kv.first, kv.second);
+ os << out.write();
+ return os;
+}
+
+inline void WriteText(const fs::path &path, const std::string &text) {
+ fsbridge::ofstream file;
+ file.open(path);
+ file << text;
+}
+
BOOST_FIXTURE_TEST_SUITE(settings_tests, BasicTestingSetup)
+BOOST_AUTO_TEST_CASE(ReadWrite) {
+ fs::path path = GetDataDir() / "settings.json";
+
+ WriteText(path, R"({
+ "string": "string",
+ "num": 5,
+ "bool": true,
+ "null": null
+ })");
+
+ std::map<std::string, util::SettingsValue> expected{
+ {"string", "string"},
+ {"num", 5},
+ {"bool", true},
+ {"null", {}},
+ };
+
+ // Check file read.
+ std::map<std::string, util::SettingsValue> values;
+ std::vector<std::string> errors;
+ BOOST_CHECK(util::ReadSettings(path, values, errors));
+ BOOST_CHECK_EQUAL_COLLECTIONS(values.begin(), values.end(),
+ expected.begin(), expected.end());
+ BOOST_CHECK(errors.empty());
+
+ // Check no errors if file doesn't exist.
+ fs::remove(path);
+ BOOST_CHECK(util::ReadSettings(path, values, errors));
+ BOOST_CHECK(values.empty());
+ BOOST_CHECK(errors.empty());
+
+ // Check duplicate keys not allowed
+ WriteText(path, R"({
+ "dupe": "string",
+ "dupe": "dupe"
+ })");
+ BOOST_CHECK(!util::ReadSettings(path, values, errors));
+ std::vector<std::string> dup_keys = {strprintf(
+ "Found duplicate key dupe in settings file %s", path.string())};
+ BOOST_CHECK_EQUAL_COLLECTIONS(errors.begin(), errors.end(),
+ dup_keys.begin(), dup_keys.end());
+
+ // Check non-kv json files not allowed
+ WriteText(path, R"("non-kv")");
+ BOOST_CHECK(!util::ReadSettings(path, values, errors));
+ std::vector<std::string> non_kv = {
+ strprintf("Found non-object value \"non-kv\" in settings file %s",
+ path.string())};
+ BOOST_CHECK_EQUAL_COLLECTIONS(errors.begin(), errors.end(), non_kv.begin(),
+ non_kv.end());
+
+ // Check invalid json not allowed
+ WriteText(path, R"(invalid json)");
+ BOOST_CHECK(!util::ReadSettings(path, values, errors));
+ std::vector<std::string> fail_parse = {
+ strprintf("Unable to parse settings file %s", path.string())};
+ BOOST_CHECK_EQUAL_COLLECTIONS(errors.begin(), errors.end(),
+ fail_parse.begin(), fail_parse.end());
+}
+
//! Check settings struct contents against expected json strings.
static void CheckValues(const util::Settings &settings,
const std::string &single_val,
diff --git a/src/util/settings.h b/src/util/settings.h
--- a/src/util/settings.h
+++ b/src/util/settings.h
@@ -5,6 +5,8 @@
#ifndef BITCOIN_UTIL_SETTINGS_H
#define BITCOIN_UTIL_SETTINGS_H
+#include <fs.h>
+
#include <map>
#include <string>
#include <vector>
@@ -37,6 +39,16 @@
ro_config;
};
+//! Read settings file.
+bool ReadSettings(const fs::path &path,
+ std::map<std::string, SettingsValue> &values,
+ std::vector<std::string> &errors);
+
+//! Write settings file.
+bool WriteSettings(const fs::path &path,
+ const std::map<std::string, SettingsValue> &values,
+ std::vector<std::string> &errors);
+
//! Get settings value from combined sources: forced settings, command line
//! arguments and the read-only config file.
//!
diff --git a/src/util/settings.cpp b/src/util/settings.cpp
--- a/src/util/settings.cpp
+++ b/src/util/settings.cpp
@@ -4,6 +4,7 @@
#include <util/settings.h>
+#include <tinyformat.h>
#include <univalue.h>
namespace util {
@@ -52,6 +53,75 @@
}
} // namespace
+bool ReadSettings(const fs::path &path,
+ std::map<std::string, SettingsValue> &values,
+ std::vector<std::string> &errors) {
+ values.clear();
+ errors.clear();
+
+ fsbridge::ifstream file;
+ file.open(path);
+ if (!file.is_open()) {
+ // Ok for file not to exist.
+ return true;
+ }
+
+ SettingsValue in;
+ if (!in.read(std::string{std::istreambuf_iterator<char>(file),
+ std::istreambuf_iterator<char>()})) {
+ errors.emplace_back(
+ strprintf("Unable to parse settings file %s", path.string()));
+ return false;
+ }
+
+ if (file.fail()) {
+ errors.emplace_back(
+ strprintf("Failed reading settings file %s", path.string()));
+ return false;
+ }
+ // Done with file descriptor. Release while copying data.
+ file.close();
+
+ if (!in.isObject()) {
+ errors.emplace_back(
+ strprintf("Found non-object value %s in settings file %s",
+ in.write(), path.string()));
+ return false;
+ }
+
+ const std::vector<std::string> &in_keys = in.getKeys();
+ const std::vector<SettingsValue> &in_values = in.getValues();
+ for (size_t i = 0; i < in_keys.size(); ++i) {
+ auto inserted = values.emplace(in_keys[i], in_values[i]);
+ if (!inserted.second) {
+ errors.emplace_back(
+ strprintf("Found duplicate key %s in settings file %s",
+ in_keys[i], path.string()));
+ }
+ }
+ return errors.empty();
+}
+
+bool WriteSettings(const fs::path &path,
+ const std::map<std::string, SettingsValue> &values,
+ std::vector<std::string> &errors) {
+ SettingsValue out(SettingsValue::VOBJ);
+ for (const auto &value : values) {
+ out.__pushKV(value.first, value.second);
+ }
+ fsbridge::ofstream file;
+ file.open(path);
+ if (file.fail()) {
+ errors.emplace_back(
+ strprintf("Error: Unable to open settings file %s for writing",
+ path.string()));
+ return false;
+ }
+ file << out.write(/* prettyIndent= */ 1, /* indentLevel= */ 4) << std::endl;
+ file.close();
+ return true;
+}
+
SettingsValue GetSetting(const Settings &settings, const std::string §ion,
const std::string &name,
bool ignore_default_section_config,
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Mar 1, 11:16 (15 h, 2 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5187552
Default Alt Text
D8011.diff (6 KB)
Attached To
D8011: util: Add ReadSettings and WriteSettings functions
Event Timeline
Log In to Comment