store: ensure updates update message sexp too
And turn all "add" into "replace" so old messages get removed. Update tests too.
This commit is contained in:
@ -190,7 +190,12 @@ Sexp::to_sexp_string() const
|
||||
sstrm << ')';
|
||||
break;
|
||||
}
|
||||
case Type::String: sstrm << quote(value()); break;
|
||||
case Type::String:
|
||||
sstrm << quote(value());
|
||||
break;
|
||||
case Type::Raw:
|
||||
sstrm << value();
|
||||
break;
|
||||
case Type::Number:
|
||||
case Type::Symbol:
|
||||
case Type::Empty:
|
||||
@ -231,7 +236,12 @@ Sexp::to_json_string() const
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Type::String: sstrm << quote(value()); break;
|
||||
case Type::String:
|
||||
sstrm << quote(value());
|
||||
break;
|
||||
case Type::Raw: // FIXME: implement this.
|
||||
break;
|
||||
|
||||
case Type::Symbol:
|
||||
if (is_nil())
|
||||
sstrm << "false";
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
** Copyright (C) 2021 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
||||
** Copyright (C) 2022 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
||||
**
|
||||
** This program is free software; you can redistribute it and/or modify it
|
||||
** under the terms of the GNU General Public License as published by the
|
||||
@ -37,7 +37,7 @@ namespace Mu {
|
||||
/// Parse node
|
||||
struct Sexp {
|
||||
/// Node type
|
||||
enum struct Type { Empty, List, String, Number, Symbol };
|
||||
enum struct Type { Empty, List, String, Number, Symbol, Raw };
|
||||
|
||||
/**
|
||||
* Default CTOR
|
||||
@ -81,13 +81,30 @@ struct Sexp {
|
||||
}
|
||||
|
||||
static Sexp make_number(int val) { return Sexp{Type::Number, format("%d", val)}; }
|
||||
static Sexp make_symbol(std::string&& val)
|
||||
{
|
||||
static Sexp make_symbol(std::string&& val) {
|
||||
if (val.empty())
|
||||
throw Error(Error::Code::InvalidArgument, "symbol must be non-empty");
|
||||
throw Error(Error::Code::InvalidArgument,
|
||||
"symbol must be non-empty");
|
||||
return Sexp{Type::Symbol, std::move(val)};
|
||||
}
|
||||
static Sexp make_symbol_sv(std::string_view val) { return make_symbol(std::string{val}); }
|
||||
static Sexp make_symbol_sv(std::string_view val) {
|
||||
return make_symbol(std::string{val});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a raw string sexp.
|
||||
*
|
||||
* @param val value
|
||||
*
|
||||
* @return A sexp
|
||||
*/
|
||||
static Sexp make_raw(std::string&& val) {
|
||||
return Sexp{Type::Raw, std::string{val}};
|
||||
}
|
||||
static Sexp make_raw(const std::string& val) {
|
||||
return make_raw(std::string{val});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
@ -96,8 +113,7 @@ struct Sexp {
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
const std::string& value() const
|
||||
{
|
||||
const std::string& value() const {
|
||||
if (is_list())
|
||||
throw Error(Error::Code::InvalidArgument, "no value for list");
|
||||
if (is_empty())
|
||||
@ -110,15 +126,14 @@ struct Sexp {
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
const Seq& list() const
|
||||
{
|
||||
const Seq& list() const {
|
||||
if (!is_list())
|
||||
throw Error(Error::Code::InvalidArgument, "not a list");
|
||||
return seq_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Sexp::Node to its S-expression string representation
|
||||
* Convert a Sexp to its S-expression string representation
|
||||
*
|
||||
* @return the string representation
|
||||
*/
|
||||
@ -142,6 +157,9 @@ struct Sexp {
|
||||
/// Helper struct to build mutable lists.
|
||||
///
|
||||
struct List {
|
||||
List () = default;
|
||||
List (const Seq& seq): seq_{seq} {}
|
||||
|
||||
/**
|
||||
* Add a sexp to the list
|
||||
*
|
||||
@ -164,7 +182,8 @@ struct Sexp {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a property (i.e., :key sexp ) to the list
|
||||
* Add a property (i.e., :key sexp ) to the list. Remove any
|
||||
* prop with the same name
|
||||
*
|
||||
* @param name a property-name. Must start with ':', length > 1
|
||||
* @param sexp a sexp
|
||||
@ -172,8 +191,8 @@ struct Sexp {
|
||||
*
|
||||
* @return a ref to this List (for chaining)
|
||||
*/
|
||||
List& add_prop(std::string&& name, Sexp&& sexp)
|
||||
{
|
||||
List& add_prop(std::string&& name, Sexp&& sexp) {
|
||||
remove_prop(name);
|
||||
if (!is_prop_name(name))
|
||||
throw Error{Error::Code::InvalidArgument,
|
||||
"invalid property name ('%s')",
|
||||
@ -183,13 +202,27 @@ struct Sexp {
|
||||
return *this;
|
||||
}
|
||||
template <typename... Args>
|
||||
List& add_prop(std::string&& name, Sexp&& sexp, Args... args)
|
||||
{
|
||||
List& add_prop(std::string&& name, Sexp&& sexp, Args... args) {
|
||||
remove_prop(name);
|
||||
add_prop(std::move(name), std::move(sexp));
|
||||
add_prop(std::forward<Args>(args)...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void remove_prop(const std::string& name) {
|
||||
if (!is_prop_name(name))
|
||||
throw Error{Error::Code::InvalidArgument,
|
||||
"invalid property name ('%s')", name.c_str()};
|
||||
auto it = std::find_if(seq_.begin(), seq_.end(), [&](auto&& elm) {
|
||||
return elm.type() == Sexp::Type::Symbol &&
|
||||
elm.value() == name;
|
||||
});
|
||||
if (it != seq_.cend() && it + 1 != seq_.cend()) {
|
||||
/* erase propname and value.*/
|
||||
seq_.erase(it, it + 2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all elements from the list.
|
||||
*/
|
||||
@ -209,7 +242,7 @@ struct Sexp {
|
||||
*/
|
||||
size_t empty() const { return seq_.empty(); }
|
||||
|
||||
private:
|
||||
private:
|
||||
friend struct Sexp;
|
||||
Seq seq_;
|
||||
};
|
||||
@ -309,16 +342,14 @@ struct Sexp {
|
||||
return is_prop_list(list().begin() + 1, list().end());
|
||||
}
|
||||
|
||||
private:
|
||||
Sexp(Type typearg, std::string&& valuearg) : type_{typearg}, value_{std::move(valuearg)}
|
||||
{
|
||||
private:
|
||||
Sexp(Type typearg, std::string&& valuearg) : type_{typearg}, value_{std::move(valuearg)} {
|
||||
if (is_list())
|
||||
throw Error{Error::Code::InvalidArgument, "cannot be a list type"};
|
||||
if (is_empty())
|
||||
throw Error{Error::Code::InvalidArgument, "cannot be an empty type"};
|
||||
}
|
||||
Sexp(Type typearg, Seq&& seq) : type_{Type::List}, seq_{std::move(seq)}
|
||||
{
|
||||
Sexp(Type typearg, Seq&& seq) : type_{Type::List}, seq_{std::move(seq)} {
|
||||
if (!is_list())
|
||||
throw Error{Error::Code::InvalidArgument, "must be a list type"};
|
||||
if (is_empty())
|
||||
@ -353,10 +384,10 @@ struct Sexp {
|
||||
return b == e;
|
||||
}
|
||||
|
||||
const Type type_; /**< Type of node */
|
||||
const std::string value_; /**< String value of node (only for
|
||||
* non-Type::Lst)*/
|
||||
const Seq seq_; /**< Children of node (only for
|
||||
Type type_; /**< Type of node */
|
||||
std::string value_; /**< String value of node (only for
|
||||
* non-Type::Lst)*/
|
||||
Seq seq_; /**< Children of node (only for
|
||||
* Type::Lst) */
|
||||
};
|
||||
|
||||
|
||||
@ -55,7 +55,7 @@ test_parser()
|
||||
|
||||
check_parse(R"("foo
|
||||
bar")",
|
||||
"\"foo\nbar\"");
|
||||
"\"foo\nbar\"");
|
||||
}
|
||||
|
||||
static void
|
||||
@ -111,20 +111,47 @@ static void
|
||||
test_props()
|
||||
{
|
||||
auto sexp2 = Sexp::make_list(Sexp::make_string("foo"),
|
||||
Sexp::make_number(123),
|
||||
Sexp::make_symbol("blub"));
|
||||
Sexp::make_number(123),
|
||||
Sexp::make_symbol("blub"));
|
||||
|
||||
auto sexp = Sexp::make_prop_list(":foo",
|
||||
Sexp::make_string("bär"),
|
||||
":cuux",
|
||||
Sexp::make_number(123),
|
||||
":flub",
|
||||
Sexp::make_symbol("fnord"),
|
||||
":boo",
|
||||
std::move(sexp2));
|
||||
Sexp::make_string("bär"),
|
||||
":cuux",
|
||||
Sexp::make_number(123),
|
||||
":flub",
|
||||
Sexp::make_symbol("fnord"),
|
||||
":boo",
|
||||
std::move(sexp2));
|
||||
|
||||
assert_equal(sexp.to_sexp_string(),
|
||||
"(:foo \"b\303\244r\" :cuux 123 :flub fnord :boo (\"foo\" 123 blub))");
|
||||
"(:foo \"b\303\244r\" :cuux 123 :flub fnord :boo (\"foo\" 123 blub))");
|
||||
}
|
||||
|
||||
static void
|
||||
test_prop_list_remove()
|
||||
{
|
||||
{
|
||||
Sexp::List lst;
|
||||
lst.add_prop(":foo", Sexp::make_string("123"))
|
||||
.add_prop(":bar", Sexp::make_number(123));
|
||||
|
||||
assert_equal(Sexp::make_list(std::move(lst)).to_sexp_string(),
|
||||
R"((:foo "123" :bar 123))");
|
||||
}
|
||||
|
||||
{
|
||||
Sexp::List lst;
|
||||
lst.add_prop(":foo", Sexp::make_string("123"))
|
||||
.add_prop(":bar", Sexp::make_number(123));
|
||||
|
||||
assert_equal(Sexp::make_list(Sexp::List{lst}).to_sexp_string(),
|
||||
R"((:foo "123" :bar 123))");
|
||||
|
||||
lst.remove_prop(":bar");
|
||||
|
||||
assert_equal(Sexp::make_list(Sexp::List{lst}).to_sexp_string(),
|
||||
R"((:foo "123"))");
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
@ -140,6 +167,7 @@ try {
|
||||
g_test_add_func("/utils/sexp/parser", test_parser);
|
||||
g_test_add_func("/utils/sexp/list", test_list);
|
||||
g_test_add_func("/utils/sexp/proplist", test_prop_list);
|
||||
g_test_add_func("/utils/sexp/proplist-remove", test_prop_list_remove);
|
||||
g_test_add_func("/utils/sexp/props", test_props);
|
||||
|
||||
return g_test_run();
|
||||
|
||||
Reference in New Issue
Block a user