Remove the always questionable Scheme generation and parsing code.

It's more reasonable to do that in Scheme than in C++.
This commit is contained in:
John Ralls 2021-08-28 16:52:36 -07:00
parent a487ca3f98
commit bed44f404f
8 changed files with 73 additions and 1173 deletions

View File

@ -220,8 +220,6 @@ public:
}
void reset_default_value() { m_value = m_default_value; }
bool is_changed() const noexcept { return m_value != m_default_value; }
std::ostream& to_scheme(std::ostream&) const;
std::istream& from_scheme(std::istream&);
GncOptionUIType get_ui_type() const noexcept { return m_ui_type; }
void make_internal() { m_ui_type = GncOptionUIType::INTERNAL; }
private:
@ -353,85 +351,6 @@ operator>> <GncOptionValue<bool>>(std::istream& iss,
opt.set_value(instr == "#t" ? true : false);
return iss;
}
template<class OptType,
typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>,
GncOptionValidatedValue<const QofInstance*>> ||
std::is_same_v<std::decay_t<OptType>,
GncOptionValue<const QofInstance*>>,
int> = 0>
inline std::ostream&
gnc_option_to_scheme (std::ostream& oss, const OptType& opt)
{
auto value = opt.get_value();
auto type = opt.get_ui_type();
if (type == GncOptionUIType::COMMODITY || type == GncOptionUIType::CURRENCY)
{
if (type == GncOptionUIType::COMMODITY)
{
oss << commodity_scm_intro;
oss << "\"" <<
gnc_commodity_get_namespace(GNC_COMMODITY(value)) << "\" ";
}
oss << "\"" << gnc_commodity_get_mnemonic(GNC_COMMODITY(value)) << "\"";
if (type == GncOptionUIType::COMMODITY)
{
oss << ")";
}
return oss;
}
if constexpr (std::is_same_v<std::decay_t<decltype(value)>, std::string>)
{
if (type == GncOptionUIType::COLOR)
return output_color_value(oss, value);
}
oss << "\"" << qof_instance_to_string(value) << "\"";
return oss;
}
template<class OptType,
typename std::enable_if_t<
std::is_same_v<std::decay_t<OptType>,
GncOptionValidatedValue<const QofInstance*>> ||
std::is_same_v<std::decay_t<OptType>,
GncOptionValue<const QofInstance*>>, int> = 0>
inline std::istream&
gnc_option_from_scheme (std::istream& iss, OptType& opt)
{
std::string instr;
auto type = opt.get_ui_type();
if (type == GncOptionUIType::COMMODITY || type == GncOptionUIType::CURRENCY)
{
std::string name_space, mnemonic;
if (type == GncOptionUIType::COMMODITY)
{
iss.ignore(strlen(commodity_scm_intro) + 1, '"');
std::getline(iss, name_space, '"');
}
else
name_space = GNC_COMMODITY_NS_CURRENCY;
iss.ignore(2, '"');
std::getline(iss, mnemonic, '"');
if (type == GncOptionUIType::COMMODITY)
iss.ignore(2, ')');
else
iss.ignore(1, '"');
instr = name_space + ":";
instr += mnemonic;
}
else
{
iss.ignore(1, '"');
std::getline(iss, instr, '"');
}
opt.set_value(qof_instance_from_string(instr, opt.get_ui_type()));
return iss;
}
#endif // SWIG
/**
@ -807,70 +726,6 @@ operator>> <GncOptionMultichoiceValue>(std::istream& iss,
return iss;
}
template<class OptType,
typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>,
GncOptionMultichoiceValue>,
int> = 0>
inline std::ostream&
gnc_option_to_scheme(std::ostream& oss, const OptType& opt)
{
auto indexes{opt.get_multiple()};
if (indexes.size() > 1)
oss << "'(";
bool first = true;
for (auto index : indexes)
{
if (first)
first = false;
else
oss << " ";
oss << "'" << opt.permissible_value(index);
}
if (indexes.size() > 1)
oss << ')';
return oss;
}
template<class OptType,
typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>,
GncOptionMultichoiceValue>,
int> = 0>
inline std::istream&
gnc_option_from_scheme(std::istream& iss, OptType& opt)
{
iss.ignore(3, '\'');
auto c{iss.peek()};
if (static_cast<char>(c) == '(')
{
GncMultichoiceOptionIndexVec values;
iss.ignore(3, '\'');
while (true)
{
std::string str;
std::getline(iss, str, ' ');
if (!str.empty())
{
if (str.back() == ')')
{
str.pop_back();
break;
}
values.push_back(opt.permissible_value_index(str.c_str()));
iss.ignore(2, '\'');
}
else
break;
}
opt.set_multiple(values);
}
else
{
std::string str;
std::getline(iss, str, ' ');
opt.set_value(str);
}
return iss;
}
using GncOptionAccountList = std::vector<const Account*>;
@ -998,54 +853,6 @@ operator>> <GncOptionAccountListValue>(std::istream& iss,
return iss;
}
template<class OptType,
typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>,
GncOptionAccountListValue>,
int> = 0>
inline std::ostream&
gnc_option_to_scheme(std::ostream& oss, const OptType& opt)
{
auto values{opt.get_value()};
oss << "'(\"";
bool first = true;
for (auto value : values)
{
if (first)
first = false;
else
oss << " \"";
oss << qof_instance_to_string(QOF_INSTANCE(value)) << '"';
}
oss << ')';
return oss;
}
template<class OptType,
typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>,
GncOptionAccountListValue>,
int> = 0>
inline std::istream&
gnc_option_from_scheme(std::istream& iss, OptType& opt)
{
GncOptionAccountList values;
iss.ignore(3, '"');
while (true)
{
std::string str;
std::getline(iss, str, '"');
if (!str.empty())
{
values.emplace_back((Account*)qof_instance_from_string(str, opt.get_ui_type()));
iss.ignore(2, '"');
}
else
break;
}
opt.set_value(values);
iss.ignore(1, ')');
return iss;
}
class GncOptionAccountSelValue : public OptionClassifier
{
public:
@ -1129,46 +936,6 @@ operator>> <GncOptionAccountSelValue>(std::istream& iss,
return iss;
}
template<class OptType,
typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>,
GncOptionAccountSelValue>,
int> = 0>
inline std::ostream&
gnc_option_to_scheme(std::ostream& oss, const OptType& opt)
{
auto value{opt.get_value()};
oss << "'(\"";
oss << qof_instance_to_string(QOF_INSTANCE(value)) << '"';
oss << ')';
return oss;
}
template<class OptType,
typename std::enable_if_t<std::is_same_v<std::decay_t<OptType>,
GncOptionAccountSelValue>,
int> = 0>
inline std::istream&
gnc_option_from_scheme(std::istream& iss, OptType& opt)
{
const Account* value;
iss.ignore(3, '"');
while (true)
{
std::string str;
std::getline(iss, str, '"');
if (!str.empty())
{
value = (Account*)qof_instance_from_string(str, opt.get_ui_type());
iss.ignore(2, '"');
}
else
break;
}
opt.set_value(value);
iss.ignore(1, ')');
return iss;
}
/** Date options
* A legal date value is a pair of either and a RelativeDatePeriod, the absolute
* flag and a time64, or for legacy purposes the absolute flag and a timespec.
@ -1312,45 +1079,4 @@ operator>> <GncOptionDateValue>(std::istream& iss,
/** QofQuery Options
*/
inline std::istream&
gnc_option_from_scheme(std::istream& iss, GncOptionValue<const GncOwner*>& opt)
{
//FIXME: Implement or maybe rethink.
return iss;
}
inline std::ostream&
gnc_option_to_scheme(std::ostream& oss, GncOptionValue<const GncOwner*>& opt)
{
//FIXME: Implement or maybe rethink.
return oss;
}
inline std::istream&
gnc_option_from_scheme(std::istream& iss, GncOptionValue<const QofQuery*>& opt)
{
//FIXME: Implement or maybe rethink.
return iss;
}
inline std::ostream&
gnc_option_to_scheme(std::ostream& oss, GncOptionValue<const QofQuery*>& opt)
{
//FIXME: Implement or maybe rethink.
return oss;
}
inline std::istream&
gnc_option_from_scheme(std::istream& iss, GncOptionValidatedValue<const QofQuery*>& opt)
{
//FIXME: Implement or maybe rethink.
return iss;
}
inline std::ostream&
gnc_option_to_scheme(std::ostream& oss, GncOptionValidatedValue<const QofQuery*>& opt)
{
//FIXME: Implement or maybe rethink.
return oss;
}
#endif //GNC_OPTION_IMPL_HPP_

View File

@ -52,28 +52,28 @@ GncOption::get_value() const
[](const auto option)->ValueType {
if constexpr (is_same_decayed_v<decltype(option.get_value()),
ValueType>)
return option.get_value();
return option.get_value();
if constexpr (is_same_decayed_v<decltype(option),
GncOptionDateValue>)
{
if constexpr (is_same_decayed_v<ValueType,
RelativeDatePeriod>)
return option.get_period();
RelativeDatePeriod>)
return option.get_period();
if constexpr (std::is_same_v<ValueType, size_t>)
return option.get_period_index();
return option.get_period_index();
return ValueType{};
}
if constexpr (is_same_decayed_v<decltype(option),
GncOptionMultichoiceValue>)
{
if constexpr (std::is_same_v<ValueType, size_t>)
return option.get_index();
return option.get_index();
if constexpr (is_same_decayed_v<ValueType,
GncMultichoiceOptionIndexVec>)
return option.get_multiple();
GncMultichoiceOptionIndexVec>)
return option.get_multiple();
}
return ValueType {};
}, *m_option);
return ValueType {};
}, *m_option);
}
template <typename ValueType> ValueType
@ -83,25 +83,25 @@ GncOption::get_default_value() const
[](const auto option)->ValueType {
if constexpr (is_same_decayed_v<decltype(option.get_value()),
ValueType>)
return option.get_default_value();
return option.get_default_value();
if constexpr (is_same_decayed_v<decltype(option),
GncOptionDateValue>)
{
if constexpr (is_same_decayed_v<ValueType,
RelativeDatePeriod>)
return option.get_default_period();
RelativeDatePeriod>)
return option.get_default_period();
if constexpr (std::is_same_v<ValueType, size_t>)
return option.get_default_period_index();
return option.get_default_period_index();
return ValueType{};
}
if constexpr
if constexpr
(is_same_decayed_v<decltype(option),
GncOptionMultichoiceValue> &&
GncOptionMultichoiceValue> &&
is_same_decayed_v<ValueType,
GncMultichoiceOptionIndexVec>)
return option.get_default_multiple();
return ValueType {};
}, *m_option);
GncMultichoiceOptionIndexVec>)
return option.get_default_multiple();
return ValueType {};
}, *m_option);
}
@ -110,26 +110,26 @@ GncOption::set_value(ValueType value)
{
std::visit(
[value](auto& option) {
if constexpr
if constexpr
(is_same_decayed_v<decltype(option.get_value()), ValueType> ||
(is_same_decayed_v<decltype(option),
GncOptionDateValue> &&
GncOptionDateValue> &&
(is_same_decayed_v<ValueType, RelativeDatePeriod> ||
std::is_same_v<ValueType, size_t>)))
option.set_value(value);
option.set_value(value);
if constexpr (is_same_decayed_v<decltype(option),
GncOptionMultichoiceValue>)
{
GncOptionMultichoiceValue>)
{
if constexpr (is_same_decayed_v<ValueType,
GncMultichoiceOptionIndexVec>)
option.set_multiple(value);
else if constexpr
(std::is_same_v<ValueType, size_t> ||
GncMultichoiceOptionIndexVec>)
option.set_multiple(value);
else if constexpr
(std::is_same_v<ValueType, size_t> ||
is_same_decayed_v<ValueType, std::string> ||
std::is_same_v<std::remove_cv<ValueType>, char*>)
option.set_value(value);
}
}, *m_option);
std::is_same_v<std::remove_cv<ValueType>, char*>)
option.set_value(value);
}
}, *m_option);
}
template <typename ValueType> void
@ -137,25 +137,25 @@ GncOption::set_default_value(ValueType value)
{
std::visit(
[value](auto& option) {
if constexpr
if constexpr
(is_same_decayed_v<decltype(option.get_value()), ValueType>||
(is_same_decayed_v<decltype(option), GncOptionDateValue> &&
(is_same_decayed_v<ValueType, RelativeDatePeriod> ||
std::is_same_v<ValueType, size_t>)))
option.set_default_value(value);
option.set_default_value(value);
if constexpr (is_same_decayed_v<decltype(option),
GncOptionMultichoiceValue>)
{
GncOptionMultichoiceValue>)
{
if constexpr (is_same_decayed_v<ValueType,
GncMultichoiceOptionIndexVec>)
GncMultichoiceOptionIndexVec>)
option.set_default_multiple(value);
else if constexpr
(std::is_same_v<ValueType, size_t> ||
else if constexpr
(std::is_same_v<ValueType, size_t> ||
is_same_decayed_v<ValueType, std::string> ||
std::is_same_v<std::remove_cv<ValueType>, char*>)
option.set_default_value(value);
}
}, *m_option);
std::is_same_v<std::remove_cv<ValueType>, char*>)
option.set_default_value(value);
}
}, *m_option);
}
void
GncOption::reset_default_value()
@ -289,11 +289,11 @@ GncOption::is_multiselect() const noexcept
return std::visit(
[](const auto& option)->bool {
if constexpr (is_same_decayed_v<decltype(option),
GncOptionAccountListValue>)
return option.is_multiselect();
else
return false;
}, *m_option);
GncOptionAccountListValue>)
return option.is_multiselect();
else
return false;
}, *m_option);
}
template<typename ValueType> bool
@ -302,19 +302,19 @@ GncOption::validate(ValueType value) const
return std::visit(
[value] (const auto& option) -> bool {
if constexpr ((is_same_decayed_v<decltype(option),
GncOptionMultichoiceValue> &&
GncOptionMultichoiceValue> &&
is_same_decayed_v<ValueType,
std::string>) ||
std::string>) ||
(is_same_decayed_v<decltype(option),
GncOptionMultichoiceValue> &&
GncOptionMultichoiceValue> &&
is_same_decayed_v<ValueType,
GncMultichoiceOptionIndexVec>) ||
GncMultichoiceOptionIndexVec>) ||
is_same_decayed_v<decltype(option),
GncOptionValidatedValue<ValueType>>)
return option.validate(value);
else
return false;
}, *m_option);
GncOptionValidatedValue<ValueType>>)
return option.validate(value);
else
return false;
}, *m_option);
}
std::size_t
@ -323,13 +323,13 @@ GncOption::num_permissible_values() const
return std::visit(
[] (const auto& option) -> size_t {
if constexpr (is_same_decayed_v<decltype(option),
GncOptionMultichoiceValue> ||
GncOptionMultichoiceValue> ||
is_same_decayed_v<decltype(option),
GncOptionDateValue>)
return option.num_permissible_values();
else
return size_t_max;
}, *m_option);
GncOptionDateValue>)
return option.num_permissible_values();
else
return size_t_max;
}, *m_option);
}
std::size_t
@ -338,13 +338,13 @@ GncOption::permissible_value_index(const char* value) const
return std::visit(
[&value] (const auto& option) -> size_t {
if constexpr (is_same_decayed_v<decltype(option),
GncOptionMultichoiceValue> ||
GncOptionMultichoiceValue> ||
is_same_decayed_v<decltype(option),
GncOptionDateValue>)
return option.permissible_value_index(value);
else
return size_t_max;;
}, *m_option);
GncOptionDateValue>)
return option.permissible_value_index(value);
else
return size_t_max;;
}, *m_option);
}
const char*
@ -424,125 +424,6 @@ GncOption::in_stream(std::istream& iss)
}, *m_option);
}
std::ostream&
GncOption::to_scheme(std::ostream& oss) const
{
return std::visit([&oss](auto& option) ->std::ostream& {
if constexpr
((std::is_same_v<std::decay_t<decltype(option)>,
GncOptionAccountListValue>) ||
(std::is_same_v<std::decay_t<decltype(option)>,
GncOptionMultichoiceValue>) ||
std::is_same_v<std::decay_t<decltype(option)>,
GncOptionValue<const QofInstance*>> ||
std::is_same_v<std::decay_t<decltype(option)>,
GncOptionValidatedValue<const QofInstance*>>)
gnc_option_to_scheme(oss, option);
else if constexpr
(std::is_same_v<std::decay_t<decltype(option)>,
GncOptionDateValue>)
oss << "'(" << option << ")";
else if constexpr
(std::is_same_v<std::decay_t<decltype(option.get_value())>,
std::string>)
{
if (option.get_ui_type() == GncOptionUIType::COLOR)
output_color_value(oss, option.get_value());
else
oss << '"' << option << '"';
}
else if constexpr
(std::is_same_v<std::decay_t<decltype(option)>,
GncOptionRangeValue<int>> ||
std::is_same_v<std::decay_t<decltype(option)>,
GncOptionRangeValue<double>>)
{
if (option.get_ui_type() == GncOptionUIType::PLOT_SIZE)
oss << "'(" << (option.is_alternate() ?
"\"percent\" . " : "\"pixels\" . ");
oss << option.get_value();
if (option.get_ui_type() == GncOptionUIType::PLOT_SIZE)
oss << ")";
}
else
oss << option;
return oss;
}, *m_option);
}
std::istream&
GncOption::from_scheme(std::istream& iss)
{
return std::visit([&iss](auto& option) -> std::istream& {
if constexpr
((std::is_same_v<std::decay_t<decltype(option)>,
GncOptionAccountListValue>) ||
(std::is_same_v<std::decay_t<decltype(option)>,
GncOptionMultichoiceValue>) ||
std::is_same_v<std::decay_t<decltype(option)>,
GncOptionValue<const GncOwner*>> ||
std::is_same_v<std::decay_t<decltype(option)>,
GncOptionValue<const QofQuery*>> ||
std::is_same_v<std::decay_t<decltype(option)>,
GncOptionValue<const QofInstance*>> ||
std::is_same_v<std::decay_t<decltype(option)>,
GncOptionValidatedValue<const QofQuery*>> ||
std::is_same_v<std::decay_t<decltype(option)>,
GncOptionValidatedValue<const QofInstance*>>)
gnc_option_from_scheme(iss, option);
else if constexpr
(std::is_same_v<std::decay_t<decltype(option)>,
GncOptionDateValue>)
{
iss.ignore(2, '(');
iss >> option;
//operator >> clears the trailing ')'
}
else if constexpr
(std::is_same_v<std::decay_t<decltype(option.get_value())>,
std::string>)
{
iss.ignore(1, '"');
std::string input;
std::getline(iss, input, '"');
option.set_value(input);
}
else if constexpr
(std::is_same_v<std::decay_t<decltype(option)>,
GncOptionRangeValue<int>> ||
std::is_same_v<std::decay_t<decltype(option)>,
GncOptionRangeValue<double>>)
{
if (option.get_ui_type() == GncOptionUIType::PLOT_SIZE)
{
iss.ignore(3, '"');
std::string alt;
iss >> alt;
option.set_alternate(
strncmp(alt.c_str(), "pixels",
strlen("pixels")) == 0);
iss.ignore(4, ' ');
}
if constexpr(std::is_same_v<std::decay_t<decltype(option)>,
GncOptionRangeValue<int>>)
{
int val;
iss >> val;
option.set_value(val);
}
else
{
double val;
iss >> val;
option.set_value(val);
}
}
else
iss >> option;
return iss;
}, *m_option);
}
GncOption*
gnc_make_SCM_option(const char* section, const char* name,
const char* key, const char* doc_string,

View File

@ -157,9 +157,6 @@ public:
void set_alternate(bool) noexcept;
std::ostream& out_stream(std::ostream& oss) const;
std::istream& in_stream(std::istream& iss);
std::ostream& to_scheme(std::ostream& oss) const;
std::istream& from_scheme(std::istream& iss);
friend GncOptionVariant& swig_get_option(GncOption*);

View File

@ -148,18 +148,10 @@ public:
return const_cast<GncOption*>(static_cast<const GncOptionDB&>(*this).find_option(section, name));
}
const GncOption* find_option(const std::string& section, const char* name) const;
std::ostream& save_to_scheme(std::ostream& oss,
const char* options_prolog) const noexcept;
std::istream& load_from_scheme(std::istream& iss) noexcept;
std::ostream& save_to_key_value(std::ostream& oss) const noexcept;
std::istream& load_from_key_value(std::istream& iss);
void save_to_kvp(QofBook* book, bool clear_book) const noexcept;
void load_from_kvp(QofBook* book) noexcept;
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);
std::ostream& save_option_key_value(std::ostream& oss,
const std::string& section,
const std::string& name) const noexcept;
@ -175,14 +167,6 @@ private:
std::function<GncOptionUIItem*()> m_get_ui_value;
std::function<void(GncOptionUIItem*)> m_set_ui_value;
static constexpr char const* const scheme_tags[]
{
"(let ((option (gnc:lookup-option ",
" ",
")))",
" ((lambda (o) (if o (gnc:option-set-value o ",
"))) option))"
};
};

View File

@ -261,26 +261,6 @@ GncOptionDB::make_internal(const char* section, const char* name)
db_opt->make_internal();
}
std::ostream&
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.c_str());
if (!db_opt || !db_opt->is_changed())
return oss;
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->to_scheme(oss);
oss << scheme_tags[4] << "\n\n";
return oss;
}
static inline bool constexpr
is_eol(char c)
{
@ -330,273 +310,6 @@ is_delim(char c)
is_single_quote(c) || is_double_quote(c) || is_semicolon(c);
}
static std::string
scan_scheme_symbol_from_streambuf(std::streambuf* sbuf)
{
std::string retval;
while(sbuf->in_avail() && !is_delim(sbuf->sgetc()))
retval += sbuf->sbumpc();
return retval;
}
#ifdef _LIBCPP_VERSION
static inline void constexpr
#else
static inline void
#endif
consume_scheme_comment(std::streambuf* sbuf)
{
while (sbuf->in_avail() && !is_eol(sbuf->sgetc()))
sbuf->sbumpc();
}
static inline std::string
scan_scheme_string_from_streambuf(std::streambuf* sbuf)
{
std::string retval{static_cast<char>(sbuf->sbumpc())};
while(sbuf->in_avail() && !is_double_quote(sbuf->sgetc()))
retval += sbuf->sbumpc();
retval += sbuf->sbumpc(); // Add the closing quote.
return retval;
}
#ifdef _LIBCPP_VERSION
static inline void constexpr
#else
static inline void
#endif
consume_scheme_whitespace(std::streambuf* sbuf)
{
while (sbuf->in_avail() && is_whitespace(sbuf->sgetc()))
sbuf->sbumpc();
}
enum class IdentType
{
NAME, //no introducing mark
CONST, //introduced with single quote
STRING, //delimited by double-quotes.
LIST, //introduced ' and delimited by parentheses
FORM //delimited by parentheses without ' introduction.
};
struct SchemeId
{
IdentType m_type;
std::string m_name;
std::vector<SchemeId> m_ids;
};
/**
* Scheme Parse Tree
* An identifier is a string and a type (name, const, string, or form).
*/
static void scan_scheme_id_from_streambuf(std::streambuf* sbuf, SchemeId& id);
static void
scan_scheme_form_from_streambuf(std::streambuf* sbuf, SchemeId& id)
{
sbuf->sbumpc();
if (!sbuf->in_avail())
return;
char c = sbuf->sgetc();
while (sbuf->in_avail() && !is_end_paren(c))
{
SchemeId next_id;
scan_scheme_id_from_streambuf(sbuf, next_id);
if (id.m_name.empty() && next_id.m_type == IdentType::NAME)
{
id.m_name = std::move(next_id.m_name);
continue;
}
id.m_ids.emplace_back(std::move(next_id));
if (!sbuf->in_avail())
{
std::string err{"End of streambuf before end of form "};
err += id.m_name;
throw std::runtime_error(err);
}
c = sbuf->sgetc();
}
sbuf->sbumpc();
}
static void
scan_scheme_list_from_streambuf(std::streambuf* sbuf, std::string& str)
{
consume_scheme_whitespace(sbuf);
if (!sbuf->in_avail())
return;
char c = sbuf->sgetc();
while (sbuf->in_avail() && !is_end_paren(c))
{
str += static_cast<char>(sbuf->sbumpc());
if (!sbuf->in_avail())
return;
c = sbuf->sgetc();
}
str += static_cast<char>(sbuf->sbumpc());
}
static void
scan_scheme_id_from_streambuf(std::streambuf* sbuf, SchemeId& id)
{
consume_scheme_whitespace(sbuf);
if (!sbuf->in_avail())
return;
auto c{sbuf->sgetc()};
switch(c)
{
case ';':
consume_scheme_comment(sbuf);
break;
case '"':
id.m_type = IdentType::STRING;
id.m_name = scan_scheme_string_from_streambuf(sbuf);
break;
case '\'':
{
std::string value{static_cast<char>(sbuf->sbumpc())};
if (sbuf->sgetc() == '(')
{
id.m_type == IdentType::LIST;
scan_scheme_list_from_streambuf(sbuf, value);
if (value.back() != ')')
throw std::runtime_error("End of streambuf before end of form ");
}
else if (sbuf->sgetc() == '"')
throw std::runtime_error("Malformed scheme particle starts '\"");
else
{
id.m_type = IdentType::CONST;
value += scan_scheme_symbol_from_streambuf(sbuf);
}
id.m_name = std::move(value);
break;
}
case '(':
id.m_type = IdentType::FORM;
scan_scheme_form_from_streambuf(sbuf, id);
break;
default:
id.m_type = IdentType::NAME;
id.m_name = scan_scheme_symbol_from_streambuf(sbuf);
break;
}
return;
}
static inline std::string
unquote_scheme_string(const std::string& str)
{
if (str.front() == '"' && str.back() == '"')
return str.substr(1, str.size() - 2);
return str;
}
static std::optional<std::reference_wrapper<const SchemeId>>
find_form(const SchemeId& toplevel, IdentType type, const char* name)
{
if (toplevel.m_type == type && toplevel.m_name == name)
return std::ref(toplevel);
for (const auto& id : toplevel.m_ids)
{
if (id.m_type == type && id.m_name == name)
return std::ref(id);
auto child{find_form(id, type, name)};
if (child)
return child;
}
return std::nullopt;
}
std::istream&
GncOptionDB::load_option_scheme(std::istream& iss)
{
auto sbuf{iss.rdbuf()};
SchemeId toplevel;
std::optional<std::reference_wrapper<const SchemeId>> lookup_id;
bool form_found = false;
while (sbuf->in_avail() && !lookup_id)
{
scan_scheme_id_from_streambuf(sbuf, toplevel);
lookup_id = find_form(toplevel, IdentType::FORM, "gnc:lookup-option");
}
if (!lookup_id)
{
iss.setstate(std::ios_base::eofbit);
return iss; // No options
}
const auto& classifier = lookup_id->get().m_ids;
if (classifier.size() != 3)
throw std::runtime_error("Malformed option classifier.");
const auto& section = unquote_scheme_string(classifier[1].m_name);
const auto& name = unquote_scheme_string(classifier[2].m_name);
auto option = find_option(section, name.c_str());
std::string option_str{section};
option_str += ':';
option_str += name;
if (!option)
{
std::string err{"Option not found: "};
err += option_str;
throw std::runtime_error(err);
}
auto value_id = find_form(toplevel, IdentType::FORM, "gnc:option-set-value");
if (!(value_id && value_id->get().m_ids.size() == 2))
{
std::string err{"Option "};
err += option_str;
throw std::runtime_error(err + " malformed value lambda form.");
}
std::istringstream value_iss{value_id->get().m_ids[1].m_name};
option->from_scheme(value_iss);
return iss;
}
std::ostream&
GncOptionDB::save_to_scheme(std::ostream& oss, const char* options_prolog) const noexcept
{
foreach_section(
[&oss, options_prolog](const GncOptionSectionPtr& section)
{
oss << "\n; Section: " << section->get_name() << "\n\n";
section->foreach_option(
[&oss, options_prolog, &section](auto& option)
{
if (!option.is_changed())
return;
oss << scheme_tags[0] << options_prolog << "\n";
oss << scheme_tags[1] << '"' << section->get_name().substr(0, classifier_size_max) << "\"\n";
oss << scheme_tags[1] << '"' << option.get_name().substr(0, classifier_size_max) << '"';
oss << scheme_tags[2] << "\n" << scheme_tags[3];
option.to_scheme(oss);
oss << scheme_tags[4] << "\n\n";
});
});
return oss;
}
std::istream&
GncOptionDB::load_from_scheme(std::istream& iss) noexcept
{
try {
while (iss.good())
load_option_scheme(iss);
iss.clear(); //unset eofbit and maybe failbit
}
catch (const std::runtime_error& err)
{
std::cerr << "Load of options from Scheme failed: " <<
err.what() << std::endl;
}
return iss;
}
std::ostream&
GncOptionDB::save_option_key_value(std::ostream& oss,
const std::string& section,

View File

@ -584,18 +584,10 @@ wrap_unique_ptr(GncOptionDBPtr, GncOptionDB);
}
%}
%ignore gnc_option_to_scheme;
%ignore gnc_option_from_scheme;
/* GncOptionDB::register_option comes in GncOption* and GncOption&&
* overloads. The latter isn't useful to SWIG, ignore it.
*/
%ignore GncOptionDB::register_option(const char*, GncOption&&);
/* GncOptionDB::save_to_scheme takes and returns a std::stream. Good luck
* converting *that* to anything useful!
*/
%ignore GncOptionDB::save_to_scheme(std::ostream&, const char*);
%ignore GncOptionDB::save_option_scheme(std::ostream&, const char*, const std::string&, const std::string&);
%ignore GncOptionDB::load_option_scheme(std::itream&);
/* The following functions are overloaded in gnc-optiondb.hpp to provide both
* GncOptionDB* and GncOptionDBPtr& versions. That confuses SWIG so ignore the
* raw-ptr version.
@ -837,6 +829,8 @@ inline SCM return_scm_value(ValueType value)
%}
%ignore GncOptionDBCallback;
%ignore operator<(const GncOption&, const GncOption&);
%ignore operator<(const GncOptionSectionPtr&, const GncOptionSectionPtr&);
%include "gnc-option-date.hpp"
%include "gnc-option.hpp"
@ -1356,12 +1350,10 @@ inline SCM return_scm_value(ValueType value)
});
}
static std::string
std::string
gnc_optiondb_save_to_scheme(GncOptionDBPtr& odb, const char* prolog)
{
std::ostringstream oss;
odb->save_to_scheme(oss, prolog);
return oss.str();
return prolog;
}
%}

View File

@ -91,26 +91,6 @@ TEST(GncOption, test_string_stream_in)
EXPECT_EQ(pepper, option.get_value<std::string>());
}
TEST(GncOption, test_string_to_scheme)
{
GncOption option("foo", "bar", "baz", "Phony Option", std::string{"waldo"});
std::ostringstream oss;
option.to_scheme(oss);
std::string scheme_str{"\""};
scheme_str += option.get_value<std::string>() + "\"";
EXPECT_EQ(oss.str(), scheme_str);
}
TEST(GncOption, test_string_from_scheme)
{
GncOption option("foo", "bar", "baz", "Phony Option", std::string{"waldo"});
std::string pepper{"pepper"};
std::string scheme_str{"\"pepper\""};
std::istringstream iss{scheme_str};
option.from_scheme(iss);
EXPECT_EQ(pepper, option.get_value<std::string>());
}
TEST(GncOption, test_int64_t_value)
{
GncOption option("foo", "bar", "baz", "Phony Option", INT64_C(123456789));
@ -136,23 +116,6 @@ TEST(GncOption, test_int64_stream_in)
EXPECT_EQ(INT64_C(987654321), option.get_value<int64_t>());
}
TEST(GncOption, test_int64_to_scheme)
{
GncOption option("foo", "bar", "baz", "Phony Option", INT64_C(123456789));
std::ostringstream oss;
option.to_scheme(oss);
EXPECT_STREQ(oss.str().c_str(), "123456789");
}
TEST(GncOption, test_int64_from_scheme)
{
GncOption option("foo", "bar", "baz", "Phony Option", INT64_C(123456789));
std::string number{"987654321"};
std::istringstream iss{number};
option.from_scheme(iss);
EXPECT_EQ(INT64_C(987654321), option.get_value<int64_t>());
}
TEST(GncOption, test_bool_stream_out)
{
GncOption option("foo", "bar", "baz", "Phony Option", false);
@ -176,28 +139,6 @@ TEST(GncOption, test_bool_stream_in)
EXPECT_FALSE(option.get_value<bool>());
}
TEST(GncOption, test_bool_to_scheme)
{
GncOption option("foo", "bar", "baz", "Phony Option", false);
std::ostringstream oss;
oss << option;
EXPECT_STREQ(oss.str().c_str(), "#f");
oss.str("");
option.set_value(true);
option.to_scheme(oss);
EXPECT_STREQ(oss.str().c_str(), "#t");
}
TEST(GncOption, test_bool_from_scheme)
{
GncOption option("foo", "bar", "baz", "Phony Option", false);
std::istringstream iss("#t");
iss >> option;
EXPECT_TRUE(option.get_value<bool>());
iss.str("#f");
option.from_scheme(iss);
EXPECT_FALSE(option.get_value<bool>());
}
class GncOptionTest : public ::testing::Test
{
@ -242,33 +183,6 @@ TEST_F(GncOptionTest, test_budget_in)
gnc_budget_destroy(budget);
}
TEST_F(GncOptionTest, test_budget_to_scheme)
{
auto budget = gnc_budget_new(m_book);
GncOption option{"foo", "bar", "baz", "Phony Option", (const QofInstance*)budget};
auto budget_guid{gnc::GUID{*qof_instance_get_guid(QOF_INSTANCE(budget))}.to_string()};
std::ostringstream oss;
budget_guid.insert(0, "\"");
budget_guid += "\"";
option.to_scheme(oss);
EXPECT_EQ(budget_guid, oss.str());
gnc_budget_destroy(budget);
}
TEST_F(GncOptionTest, test_budget_from_scheme)
{
auto budget = gnc_budget_new(m_book);
auto budget_guid{gnc::GUID{*qof_instance_get_guid(QOF_INSTANCE(budget))}.to_string()};
budget_guid.insert(0, "\"");
budget_guid += "\"";
std::istringstream iss{budget_guid};
GncOption option{GncOptionValue<const QofInstance*>{"foo", "bar", "baz", "Phony Option", nullptr, GncOptionUIType::BUDGET}};
option.from_scheme(iss);
EXPECT_EQ(QOF_INSTANCE(budget), option.get_value<const QofInstance*>());
gnc_budget_destroy(budget);
}
TEST_F(GncOptionTest, test_commodity_ctor)
{
auto hpe = gnc_commodity_new(m_book, "Hewlett Packard Enterprise, Inc.",
@ -462,50 +376,6 @@ TEST_F(GncOptionCommodityTest, test_commodity_in)
EXPECT_EQ(QOF_INSTANCE(m_hpe), option.get_value<const QofInstance*>());
}
TEST_F(GncOptionCommodityTest, test_currency_to_scheme)
{
auto option = make_currency_option("foo", "bar", "baz", "Phony Option",
m_eur, true);
std::string eur_str{make_currency_SCM_str(m_eur)};
std::ostringstream oss;
option.to_scheme(oss);
EXPECT_EQ(eur_str, oss.str());
}
TEST_F(GncOptionCommodityTest, test_commodity_to_scheme)
{
GncOption option{"foo", "bar", "baz", "Phony Option", (const QofInstance*)m_hpe,
GncOptionUIType::COMMODITY};
std::string hpe_str{make_commodity_SCM_str(m_hpe)};
std::ostringstream oss;
option.to_scheme(oss);
EXPECT_EQ(hpe_str, oss.str());
}
TEST_F(GncOptionCommodityTest, test_currency_from_scheme)
{
auto option = make_currency_option("foo", "bar", "baz", "Phony Option",
m_eur, true);
std::string usd_str{make_currency_SCM_str(m_usd)};
std::istringstream iss{usd_str};
option.from_scheme(iss);
EXPECT_EQ(QOF_INSTANCE(m_usd), option.get_value<const QofInstance*>());
}
TEST_F(GncOptionCommodityTest, test_commodity_from_scheme)
{
GncOption option{"foo", "bar", "baz", "Phony Option", (const QofInstance*)m_aapl,
GncOptionUIType::COMMODITY};
std::string hpe_str{make_commodity_SCM_str(m_hpe)};
std::istringstream iss{hpe_str};
option.from_scheme(iss);
EXPECT_EQ(QOF_INSTANCE(m_hpe), option.get_value<const QofInstance*>());
}
class GncUIType
{
public:
@ -891,64 +761,6 @@ make_account_list_SCM_str(const GncOptionAccountList& acclist)
return retval;
}
TEST_F(GncOptionAccountTest, test_account_list_to_scheme)
{
GncOptionAccountList acclist{list_of_types({ACCT_TYPE_BANK})};
GncOption option{GncOptionAccountListValue {"foo", "bar", "baz", "Bogus Option",
GncOptionUIType::ACCOUNT_LIST,
acclist}};
std::ostringstream oss;
std::string acc_guids{make_account_list_SCM_str(acclist)};
option.to_scheme(oss);
EXPECT_EQ(acc_guids, oss.str());
GncOptionAccountList accsel{acclist[0]};
GncOption sel_option{GncOptionAccountListValue{"foo", "bar", "baz",
"Bogus Option",
GncOptionUIType::ACCOUNT_LIST,
accsel,
GncOptionAccountTypeList{ACCT_TYPE_BANK}}};
acc_guids = make_account_list_SCM_str(accsel);
oss.str("");
sel_option.to_scheme(oss);
EXPECT_EQ(acc_guids, oss.str());
}
TEST_F(GncOptionAccountTest, test_account_list_from_scheme)
{
GncOptionAccountList acclist{list_of_types({ACCT_TYPE_BANK})};
GncOption option{GncOptionAccountListValue{"foo", "bar", "baz", "Bogus Option",
GncOptionUIType::ACCOUNT_LIST,
acclist}};
std::string acc_guids{make_account_list_SCM_str(acclist)};
std::istringstream iss{acc_guids};
option.from_scheme(iss);
EXPECT_EQ(acclist, option.get_value<GncOptionAccountList>());
GncOptionAccountList accsel{acclist[0]};
GncOption sel_option{GncOptionAccountListValue{"foo", "bar", "baz",
"Bogus Option",
GncOptionUIType::ACCOUNT_LIST,
accsel,
GncOptionAccountTypeList{ACCT_TYPE_BANK}}};
GncOptionAccountList acclistbad{list_of_types({ACCT_TYPE_STOCK})};
acc_guids = make_account_list_SCM_str(acclistbad);
iss.clear();
iss.str(acc_guids);
sel_option.from_scheme(iss);
EXPECT_EQ(accsel, sel_option.get_value<GncOptionAccountList>());
iss.clear(); //Reset the failedbit from the invalid selection type.
acc_guids = make_account_list_SCM_str(GncOptionAccountList{acclist[1]});
EXPECT_NO_THROW({
iss.str(acc_guids);
sel_option.from_scheme(iss);
});
EXPECT_EQ(acclist[1], sel_option.get_value<GncOptionAccountList>()[0]);
}
using KT = GncOptionMultichoiceKeyType;
class GncOptionMultichoiceTest : public ::testing::Test
@ -1031,22 +843,6 @@ TEST_F(GncMultichoiceOption, test_multichoice_in)
EXPECT_EQ(iss.str(), m_option.get_value<std::string>());
}
TEST_F(GncMultichoiceOption, test_multichoice_scheme_out)
{
std::ostringstream oss;
m_option.to_scheme(oss);
std::string scm_str{'\''};
scm_str += m_option.get_value<std::string>();
EXPECT_EQ(scm_str, oss.str());
}
TEST_F(GncMultichoiceOption, test_multichoice_scheme_in)
{
std::istringstream iss{"'pork"};
m_option.from_scheme(iss);
EXPECT_STREQ("pork", m_option.get_value<std::string>().c_str());
}
class GncOptionListTest : public ::testing::Test
{
protected:
@ -1116,26 +912,6 @@ TEST_F(GncListOption, test_list_in)
EXPECT_EQ(iss.str(), m_option.get_value<std::string>());
}
TEST_F(GncListOption, test_list_scheme_out)
{
std::ostringstream oss;
m_option.to_scheme(oss);
std::string value{"'('"};
auto vec{m_option.get_value<GncMultichoiceOptionIndexVec>()};
value += m_option.permissible_value(vec[0]);
value += " '";
value += m_option.permissible_value(vec[1]);
value += ")";
EXPECT_EQ(value, oss.str());
}
TEST_F(GncListOption, test_list_scheme_in)
{
std::istringstream iss{"'('pork 'waldo)"};
m_option.from_scheme(iss);
EXPECT_STREQ("pork", m_option.get_value<std::string>().c_str());
}
static time64
time64_from_gdate(const GDate* g_date, DayPart when)
{
@ -1572,63 +1348,3 @@ TEST_F(GncDateOption, test_stream_in_prev_year_end)
EXPECT_EQ(time1, m_option.get_value<time64>());
}
/* We only need wiggle tests for to_scheme and from_scheme as they just wrap or
unwrap the normal stream output with "'(" and ")".
*/
TEST_F(GncDateOption, test_date_option_to_scheme)
{
time64 time1{static_cast<time64>(GncDateTime("2019-07-19 15:32:26 +05:00"))};
m_option.set_value(time1);
std::ostringstream oss;
oss << time1;
std::string timestr{"'(absolute . "};
timestr += oss.str();
timestr += ')';
oss.str("");
m_option.to_scheme(oss);
EXPECT_EQ(oss.str(), timestr);
m_option.set_value(RelativeDatePeriod::TODAY);
oss.str("");
m_option.to_scheme(oss);
EXPECT_STREQ(oss.str().c_str(), "'(relative . today)");
m_option.set_value(RelativeDatePeriod::START_THIS_MONTH);
oss.str("");
m_option.to_scheme(oss);
EXPECT_STREQ(oss.str().c_str(), "'(relative . start-this-month)");
}
TEST_F(GncDateOption, test_date_option_from_scheme)
{
time64 time1{static_cast<time64>(GncDateTime("2019-07-19 15:32:26 +05:00"))};
std::ostringstream oss;
oss << time1;
std::string timestr{"'(absolute . "};
timestr += oss.str();
timestr += ')';
std::istringstream iss{timestr};
m_option.from_scheme(iss);
EXPECT_EQ(time1, m_option.get_value<time64>());
GDate month_start;
g_date_set_time_t(&month_start, time(nullptr));
gnc_gdate_set_month_start(&month_start);
time1 = time64_from_gdate(&month_start, DayPart::start);
iss.clear();
iss.str("'(relative . start-this-month)");
m_option.from_scheme(iss);
EXPECT_EQ(time1, m_option.get_value<time64>());
GDate date;
g_date_set_time_t(&date, time(nullptr));
gnc_gdate_set_month_end(&date);
time1 = time64_from_gdate(&date, DayPart::end);
iss.clear();
iss.str("'(relative . end-this-month)");
m_option.from_scheme(iss);
EXPECT_EQ(time1, m_option.get_value<time64>());
}

View File

@ -337,115 +337,6 @@ protected:
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")->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_string_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_date_interval_option_scheme_input)
{
const char* input{"(let ((option (gnc:lookup-option option\n"
" \"pork\"\n"
" \"garply\")))\n"
" ((lambda (o) (if o (gnc:option-set-value o "
"'(relative . end-prev-month)"
"))) option))\n\n"};
std::istringstream iss{input};
GDate month_end;
g_date_set_time_t(&month_end, time(nullptr));
g_date_subtract_months(&month_end, 1);
gnc_gdate_set_month_end(&month_end);
auto time1 = time64_from_gdate(&month_end, DayPart::end);
m_db->load_option_scheme(iss);
EXPECT_EQ(time1, m_db->find_option("pork", "garply")->get_value<time64>());
}
TEST_F(GncOptionDBIOTest, test_account_list_option_scheme_input)
{
auto acclist{gnc_account_list_from_types(m_book, {ACCT_TYPE_STOCK})};
auto hpe_guid{qof_instance_to_string(QOF_INSTANCE(acclist[3]))};
auto msft_guid{qof_instance_to_string(QOF_INSTANCE(acclist[2]))};
std::string input{"(let ((option (gnc:lookup-option option\n"
" \"quux\"\n"
" \"xyzzy\")))\n"
" ((lambda (o) (if o (gnc:option-set-value o '(\""};
input += hpe_guid + "\" \"";
input += msft_guid + "\")))) option))\n\n";
std::istringstream iss{input};
EXPECT_EQ(acclist[1], m_db->find_option("quux", "xyzzy")->get_value<GncOptionAccountList>()[0]);
m_db->load_option_scheme(iss);
EXPECT_EQ(acclist[2], m_db->find_option("quux", "xyzzy")->get_value<GncOptionAccountList>()[1]);
EXPECT_EQ(2u, m_db->find_option("quux", "xyzzy")->get_value<GncOptionAccountList>().size());
}
TEST_F(GncOptionDBIOTest, test_multiple_options_scheme_input)
{
auto acclist{gnc_account_list_from_types(m_book, {ACCT_TYPE_STOCK})};
auto hpe_guid{qof_instance_to_string(QOF_INSTANCE(acclist[3]))};
auto msft_guid{qof_instance_to_string(QOF_INSTANCE(acclist[2]))};
std::string input{";; Foo\n\n"
"(let ((option (gnc:lookup-option option\n"
" \"foo\"\n"
" \"sausage\")))\n"
" ((lambda (o) (if o (gnc:option-set-value o \"pepper\""
"))) option))\n\n"
";; Pork\n\n"
"(let ((option (gnc:lookup-option option\n"
" \"pork\"\n"
" \"garply\")))\n"
" ((lambda (o) (if o (gnc:option-set-value o "
"'(relative . end-prev-month)"
"))) option))\n\n"
";; Quux\n\n"
"(let ((option (gnc:lookup-option option\n"
" \"quux\"\n"
" \"xyzzy\")))\n"
" ((lambda (o) (if o (gnc:option-set-value o '(\""};
input += hpe_guid + "\" \"";
input += msft_guid + "\")))) option))\n\n";
std::istringstream iss{input};
GDate month_end;
g_date_set_time_t(&month_end, time(nullptr));
g_date_subtract_months(&month_end, 1);
gnc_gdate_set_month_end(&month_end);
auto time1 = time64_from_gdate(&month_end, DayPart::end);
EXPECT_EQ(acclist[1], m_db->find_option("quux", "xyzzy")->get_value<GncOptionAccountList>()[0]);
m_db->load_from_scheme(iss);
EXPECT_STREQ("pepper", m_db->lookup_string_option("foo", "sausage").c_str());
EXPECT_EQ(time1, m_db->find_option("pork", "garply")->get_value<time64>());
EXPECT_EQ(acclist[2], m_db->find_option("quux", "xyzzy")->get_value<GncOptionAccountList>()[1]);
EXPECT_EQ(2u, m_db->find_option("quux", "xyzzy")->get_value<GncOptionAccountList>().size());
}
TEST_F(GncOptionDBIOTest, test_option_key_value_output)
{
std::ostringstream oss;