mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
Implement saving and loading OptionDB items to/from scheme and
key-value string representations.
This commit is contained in:
@@ -22,7 +22,10 @@
|
||||
\********************************************************************/
|
||||
|
||||
#include "gnc-optiondb.hpp"
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
|
||||
auto constexpr stream_max = std::numeric_limits<std::streamsize>::max();
|
||||
GncOptionDB::GncOptionDB() : m_default_section{std::nullopt} {}
|
||||
|
||||
GncOptionDB::GncOptionDB(QofBook* book) : GncOptionDB() {}
|
||||
@@ -175,24 +178,53 @@ GncOptionDB::make_internal(const char* section, const char* name)
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
GncOptionDB::serialize_option_scheme(std::ostream& oss,
|
||||
const char* option_prolog,
|
||||
const char* section, const char* name) const noexcept
|
||||
GncOptionDB::save_option_scheme(std::ostream& oss,
|
||||
const char* option_prolog,
|
||||
const std::string& section,
|
||||
const std::string& name) const noexcept
|
||||
{
|
||||
auto db_opt = find_option(section, name);
|
||||
|
||||
if (!db_opt || !db_opt->get().is_changed())
|
||||
return oss;
|
||||
oss << c_scheme_serialization_elements[0] << option_prolog;
|
||||
oss << c_scheme_serialization_elements[1] << section;
|
||||
oss << c_scheme_serialization_elements[1] << name; //repeats, not an error!
|
||||
// oss << c_scheme_serialization_elements[2] << db_opt->get();
|
||||
oss << c_scheme_serialization_elements[3];
|
||||
oss << scheme_tags[0] << option_prolog << "\n";
|
||||
oss << scheme_tags[1] << '"' << section.substr(0, classifier_size_max) << "\"\n";
|
||||
oss << scheme_tags[1] << '"' << name.substr(0, classifier_size_max) << '"';
|
||||
oss << scheme_tags[2] << "\n" << scheme_tags[3];
|
||||
db_opt->get().to_scheme(oss);
|
||||
oss << scheme_tags[4] << "\n\n";
|
||||
|
||||
return oss;
|
||||
}
|
||||
std::istream&
|
||||
GncOptionDB::load_option_scheme(std::istream& iss) noexcept
|
||||
{
|
||||
std::string section{classifier_size_max};
|
||||
std::string name{classifier_size_max};
|
||||
iss.ignore(strlen(scheme_tags[0]) + 7, '\n'); //throw away the scheme noise;
|
||||
iss >> section; // Whitespace automatically discarded.
|
||||
iss >> name; // Ditto
|
||||
if (section.size() > 2)
|
||||
section = section.substr(1, section.size() - 2); //Trim the quotes
|
||||
if (name.size() > 2 + strlen(scheme_tags[2]))
|
||||
name = name.substr(1, name.size() - (2 + strlen(scheme_tags[2])));
|
||||
auto option = find_option(section.c_str(), name.c_str());
|
||||
if (!option)
|
||||
{
|
||||
std::cerr << "Option " << section << ":" << name << " not found." << std::endl;
|
||||
iss.ignore(stream_max, '\n'); // No option, discard the line
|
||||
iss.ignore(stream_max, '\n'); // And the trailing newline
|
||||
return iss;
|
||||
}
|
||||
iss.ignore(strlen(scheme_tags[2]) +1, '\n');
|
||||
iss.ignore(strlen(scheme_tags[3]));
|
||||
option->get().from_scheme(iss);
|
||||
iss.ignore(strlen(scheme_tags[4]) + 2, '\n'); //discard the noise at the end.
|
||||
return iss;
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
GncOptionDB::serialize_option_key_value(std::ostream& oss,
|
||||
GncOptionDB::save_option_key_value(std::ostream& oss,
|
||||
const char* section,
|
||||
const char* name) const noexcept
|
||||
{
|
||||
@@ -200,13 +232,30 @@ GncOptionDB::serialize_option_key_value(std::ostream& oss,
|
||||
auto db_opt = find_option(section, name);
|
||||
if (!db_opt || !db_opt->get().is_changed())
|
||||
return oss;
|
||||
oss << section << ":" << name << "=" /* << db_opt->get() */ << ";";
|
||||
oss << section << ":" << name << "=" << db_opt->get() << ";";
|
||||
return oss;
|
||||
}
|
||||
|
||||
void
|
||||
GncOptionDB::commit()
|
||||
std::istream&
|
||||
GncOptionDB::load_option_key_value(std::istream& iss)
|
||||
{
|
||||
|
||||
char section[classifier_size_max], name[classifier_size_max];
|
||||
iss.getline(section, classifier_size_max, ':');
|
||||
iss.getline(name, classifier_size_max, '=');
|
||||
if (!iss)
|
||||
throw std::invalid_argument("Section or name delimiter not found or values too long");
|
||||
auto option = find_option(section, name);
|
||||
if (!option)
|
||||
iss.ignore(stream_max, ';');
|
||||
else
|
||||
{
|
||||
std::string value;
|
||||
std::getline(iss, value, ';');
|
||||
std::istringstream item_iss{value};
|
||||
item_iss >> option->get();
|
||||
}
|
||||
return iss;
|
||||
}
|
||||
|
||||
GncOptionDBPtr
|
||||
|
||||
@@ -87,30 +87,31 @@ public:
|
||||
std::optional<std::reference_wrapper<GncOption>> find_option(const std::string& section, const std::string& name) {
|
||||
return static_cast<const GncOptionDB&>(*this).find_option(section, name);
|
||||
}
|
||||
private:
|
||||
std::ostream& serialize_option_scheme(std::ostream& oss,
|
||||
std::optional<std::reference_wrapper<GncOption>> find_option(const std::string& section, const std::string& name) const;
|
||||
const char* option_prolog,
|
||||
const char* section, const char* name) const noexcept;
|
||||
std::ostream& serialize_option_key_value(std::ostream& oss,
|
||||
std::ostream& save_option_scheme(std::ostream& oss,
|
||||
const char* option_prolog,
|
||||
const std::string& section,
|
||||
const std::string& name) const noexcept;
|
||||
std::istream& load_option_scheme(std::istream& iss) noexcept;
|
||||
std::ostream& save_option_key_value(std::ostream& oss,
|
||||
const char* section,
|
||||
const char* name) const noexcept;
|
||||
void load_option_scheme(std::istream iss);
|
||||
void load_option_key_value(std::istream iss);
|
||||
std::istream& load_option_key_value(std::istream& iss);
|
||||
private:
|
||||
std::optional<std::reference_wrapper<GncOptionSection>> m_default_section;
|
||||
std::vector<GncOptionSection> m_sections;
|
||||
bool m_dirty = false;
|
||||
|
||||
std::function<GncOptionUIItem*()> m_get_ui_value;
|
||||
std::function<void(GncOptionUIItem*)> m_set_ui_value;
|
||||
static constexpr char const* const c_scheme_serialization_elements[]
|
||||
static constexpr char const* const scheme_tags[]
|
||||
{
|
||||
"(let ((option (gnc:lookup-option ",
|
||||
"\n ",
|
||||
")))\n ((lambda (o) (if o (gnc:option-set-value o",
|
||||
"))) option))\n\n"
|
||||
" ",
|
||||
")))",
|
||||
" ((lambda (o) (if o (gnc:option-set-value o ",
|
||||
"))) option))"
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -276,3 +276,72 @@ TEST_F(GncOptionDBUITest, test_option_value_from_ui)
|
||||
});
|
||||
EXPECT_STREQ(value, m_db->lookup_string_option("foo", "bar").c_str());
|
||||
}
|
||||
|
||||
class GncOptionDBIOTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
GncOptionDBIOTest() : m_db{gnc_option_db_new()}
|
||||
{
|
||||
gnc_register_string_option(m_db, "foo", "bar", "baz", "Phony Option",
|
||||
std::string{"waldo"});
|
||||
gnc_register_text_option(m_db, "foo", "sausage", "links",
|
||||
"Phony Option", std::string{"waldo"});
|
||||
gnc_register_string_option(m_db, "qux", "grault", "baz", "Phony Option",
|
||||
std::string{""});
|
||||
gnc_register_text_option(m_db, "qux", "garply", "fred",
|
||||
"Phony Option", std::string{"waldo"});
|
||||
}
|
||||
|
||||
GncOptionDBPtr m_db;
|
||||
};
|
||||
|
||||
TEST_F(GncOptionDBIOTest, test_option_scheme_output)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
m_db->save_option_scheme(oss, "option", "foo", "sausage");
|
||||
EXPECT_STREQ("", oss.str().c_str());
|
||||
oss.clear();
|
||||
m_db->set_option("foo", "sausage", std::string{"pepper"});
|
||||
EXPECT_STREQ("pepper", m_db->lookup_string_option("foo", "sausage").c_str());
|
||||
EXPECT_TRUE(m_db->find_option("foo", "sausage")->get().is_changed());
|
||||
oss.flush();
|
||||
m_db->save_option_scheme(oss, "option", "foo", "sausage");
|
||||
EXPECT_STREQ("(let ((option (gnc:lookup-option option\n"
|
||||
" \"foo\"\n"
|
||||
" \"sausage\")))\n"
|
||||
" ((lambda (o) (if o (gnc:option-set-value o \"pepper\""
|
||||
"))) option))\n\n", oss.str().c_str());
|
||||
}
|
||||
|
||||
TEST_F(GncOptionDBIOTest, test_option_scheme_input)
|
||||
{
|
||||
const char* input{"(let ((option (gnc:lookup-option option\n"
|
||||
" \"foo\"\n"
|
||||
" \"sausage\")))\n"
|
||||
" ((lambda (o) (if o (gnc:option-set-value o \"pepper\""
|
||||
"))) option))\n\n"};
|
||||
std::istringstream iss{input};
|
||||
EXPECT_STREQ("waldo", m_db->lookup_string_option("foo", "sausage").c_str());
|
||||
m_db->load_option_scheme(iss);
|
||||
EXPECT_STREQ("pepper", m_db->lookup_string_option("foo", "sausage").c_str());
|
||||
}
|
||||
|
||||
TEST_F(GncOptionDBIOTest, test_option_key_value_output)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
m_db->save_option_key_value(oss, "foo", "sausage");
|
||||
EXPECT_STREQ("", oss.str().c_str());
|
||||
m_db->set_option("foo", "sausage", std::string{"pepper"});
|
||||
// EXPECT_STREQ("pepper", m_db->lookup_string_option("foo", "sausage").c_str());
|
||||
// EXPECT_TRUE(m_db->find_option("foo", "sausage")->get().is_changed());
|
||||
m_db->save_option_key_value(oss, "foo", "sausage");
|
||||
EXPECT_STREQ("foo:sausage=pepper;", oss.str().c_str());
|
||||
}
|
||||
|
||||
TEST_F(GncOptionDBIOTest, test_option_key_value_input)
|
||||
{
|
||||
std::istringstream iss{"foo:sausage=pepper;"};
|
||||
EXPECT_STREQ("waldo", m_db->lookup_string_option("foo", "sausage").c_str());
|
||||
m_db->load_option_key_value(iss);
|
||||
EXPECT_STREQ("pepper", m_db->lookup_string_option("foo", "sausage").c_str());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user