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 "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() : m_default_section{std::nullopt} {}
|
||||||
|
|
||||||
GncOptionDB::GncOptionDB(QofBook* book) : GncOptionDB() {}
|
GncOptionDB::GncOptionDB(QofBook* book) : GncOptionDB() {}
|
||||||
@@ -175,24 +178,53 @@ GncOptionDB::make_internal(const char* section, const char* name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::ostream&
|
std::ostream&
|
||||||
GncOptionDB::serialize_option_scheme(std::ostream& oss,
|
GncOptionDB::save_option_scheme(std::ostream& oss,
|
||||||
const char* option_prolog,
|
const char* option_prolog,
|
||||||
const char* section, const char* name) const noexcept
|
const std::string& section,
|
||||||
|
const std::string& name) const noexcept
|
||||||
{
|
{
|
||||||
auto db_opt = find_option(section, name);
|
auto db_opt = find_option(section, name);
|
||||||
|
|
||||||
if (!db_opt || !db_opt->get().is_changed())
|
if (!db_opt || !db_opt->get().is_changed())
|
||||||
return oss;
|
return oss;
|
||||||
oss << c_scheme_serialization_elements[0] << option_prolog;
|
oss << scheme_tags[0] << option_prolog << "\n";
|
||||||
oss << c_scheme_serialization_elements[1] << section;
|
oss << scheme_tags[1] << '"' << section.substr(0, classifier_size_max) << "\"\n";
|
||||||
oss << c_scheme_serialization_elements[1] << name; //repeats, not an error!
|
oss << scheme_tags[1] << '"' << name.substr(0, classifier_size_max) << '"';
|
||||||
// oss << c_scheme_serialization_elements[2] << db_opt->get();
|
oss << scheme_tags[2] << "\n" << scheme_tags[3];
|
||||||
oss << c_scheme_serialization_elements[3];
|
db_opt->get().to_scheme(oss);
|
||||||
|
oss << scheme_tags[4] << "\n\n";
|
||||||
|
|
||||||
return oss;
|
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&
|
std::ostream&
|
||||||
GncOptionDB::serialize_option_key_value(std::ostream& oss,
|
GncOptionDB::save_option_key_value(std::ostream& oss,
|
||||||
const char* section,
|
const char* section,
|
||||||
const char* name) const noexcept
|
const char* name) const noexcept
|
||||||
{
|
{
|
||||||
@@ -200,13 +232,30 @@ GncOptionDB::serialize_option_key_value(std::ostream& oss,
|
|||||||
auto db_opt = find_option(section, name);
|
auto db_opt = find_option(section, name);
|
||||||
if (!db_opt || !db_opt->get().is_changed())
|
if (!db_opt || !db_opt->get().is_changed())
|
||||||
return oss;
|
return oss;
|
||||||
oss << section << ":" << name << "=" /* << db_opt->get() */ << ";";
|
oss << section << ":" << name << "=" << db_opt->get() << ";";
|
||||||
return oss;
|
return oss;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
std::istream&
|
||||||
GncOptionDB::commit()
|
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
|
GncOptionDBPtr
|
||||||
|
|||||||
@@ -87,30 +87,31 @@ public:
|
|||||||
std::optional<std::reference_wrapper<GncOption>> find_option(const std::string& section, const std::string& name) {
|
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);
|
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;
|
std::optional<std::reference_wrapper<GncOption>> find_option(const std::string& section, const std::string& name) const;
|
||||||
const char* option_prolog,
|
std::ostream& save_option_scheme(std::ostream& oss,
|
||||||
const char* section, const char* name) const noexcept;
|
const char* option_prolog,
|
||||||
std::ostream& serialize_option_key_value(std::ostream& oss,
|
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* section,
|
||||||
const char* name) const noexcept;
|
const char* name) const noexcept;
|
||||||
void load_option_scheme(std::istream iss);
|
std::istream& load_option_key_value(std::istream& iss);
|
||||||
void load_option_key_value(std::istream iss);
|
private:
|
||||||
std::optional<std::reference_wrapper<GncOptionSection>> m_default_section;
|
std::optional<std::reference_wrapper<GncOptionSection>> m_default_section;
|
||||||
std::vector<GncOptionSection> m_sections;
|
std::vector<GncOptionSection> m_sections;
|
||||||
bool m_dirty = false;
|
bool m_dirty = false;
|
||||||
|
|
||||||
std::function<GncOptionUIItem*()> m_get_ui_value;
|
std::function<GncOptionUIItem*()> m_get_ui_value;
|
||||||
std::function<void(GncOptionUIItem*)> m_set_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 ",
|
"(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());
|
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