summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDirk-Jan C. Binnema <djcb@djcbsoftware.nl>2022-05-06 22:07:15 +0300
committerDirk-Jan C. Binnema <djcb@djcbsoftware.nl>2022-05-06 22:17:53 +0300
commitda8489d0f6bb15d14fba3cc9cf4de50eb9b1fbde (patch)
treeb97b5015a1ae0b7aacb475be0c708e5b29769457
parent2a5c1e239c6eb5ed90cf08a473e1973888ea34a2 (diff)
sexp: allow for some prettified string output
Allow for adding newlines between list items
-rw-r--r--lib/mu-server.cc30
-rw-r--r--lib/mu-server.hh22
-rw-r--r--lib/utils/mu-sexp.cc7
-rw-r--r--lib/utils/mu-sexp.hh29
-rw-r--r--mu/mu-cmd-server.cc25
5 files changed, 86 insertions, 27 deletions
diff --git a/lib/mu-server.cc b/lib/mu-server.cc
index 9c3db9f..e6a4571 100644
--- a/lib/mu-server.cc
+++ b/lib/mu-server.cc
@@ -83,14 +83,15 @@ struct Server::Private {
//
// output
//
- void output_sexp(Sexp&& sexp, bool flush = false) const
+ void output_sexp(Sexp&& sexp,Server::OutputFlags flags = {}) const
{
if (output_)
- output_(std::move(sexp), flush);
+ output_(std::move(sexp), flags);
}
- void output_sexp(Sexp::List&& lst, bool flush = false) const
+
+ void output_sexp(Sexp::List&& lst, Server::OutputFlags flags = {}) const
{
- output_sexp(Sexp::make_list(std::move(lst)), flush);
+ output_sexp(Sexp::make_list(std::move(lst)), flags);
}
size_t output_results(const QueryResults& qres, size_t batch_size) const;
@@ -520,6 +521,7 @@ Server::Private::contacts_handler(const Parameters& params)
const auto personal = get_bool_or(params, ":personal");
const auto afterstr = get_string_or(params, ":after");
const auto tstampstr = get_string_or(params, ":tstamp");
+ const auto maxnum = get_int_or(params, ":maxnum", 0 /*unlimited*/);
const auto after{afterstr.empty() ? 0 :
parse_date_time(afterstr, true).value_or(0)};
@@ -550,18 +552,20 @@ Server::Private::contacts_handler(const Parameters& params)
contact.add_prop(":address",
Sexp::make_string(ci.display_name(true/*encode-if-needed*/)));
contact.add_prop(":rank", Sexp::make_number(rank));
-
- contacts.add(Sexp::make_list(std::move(contact)));
- });
+ auto contacts_sexp{Sexp::make_list(std::move(contact))};
+ contacts_sexp.formatting_opts |= Sexp::FormattingOptions::SplitList;
+ contacts.add(std::move(contacts_sexp));
+ }, static_cast<size_t>(maxnum));
Sexp::List seq;
seq.add_prop(":contacts", Sexp::make_list(std::move(contacts)));
seq.add_prop(":tstamp",
Sexp::make_string(format("%" G_GINT64_FORMAT,
g_get_monotonic_time())));
+
/* dump the contacts cache as a giant sexp */
g_debug("sending %d of %zu contact(s)", rank, store().contacts_cache().size());
- output_sexp(std::move(seq));
+ output_sexp(std::move(seq), Server::OutputFlags::SplitList);
}
/* get a *list* of all messages with the given message id */
@@ -651,7 +655,9 @@ Server::Private::output_results(const QueryResults& qres, size_t batch_size) con
// construct sexp for a single header.
auto qm{mi.query_match()};
- headers.add(build_message_sexp(*msg, mi.doc_id(), qm));
+ auto msgsexp{build_message_sexp(*msg, mi.doc_id(), qm)};
+ msgsexp.formatting_opts |= Sexp::FormattingOptions::SplitList;
+ headers.add(std::move(msgsexp));
// we output up-to-batch-size lists of messages. It's much
// faster (on the emacs side) to handle such batches than single
// headers.
@@ -804,9 +810,11 @@ Server::Private::index_handler(const Parameters& params)
indexer().start(conf);
while (indexer().is_running()) {
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
- output_sexp(get_stats(indexer().progress(), "running"), true);
+ output_sexp(get_stats(indexer().progress(), "running"),
+ Server::OutputFlags::Flush);
}
- output_sexp(get_stats(indexer().progress(), "complete"), true);
+ output_sexp(get_stats(indexer().progress(), "complete"),
+ Server::OutputFlags::Flush);
});
}
diff --git a/lib/mu-server.hh b/lib/mu-server.hh
index 811ad6d..95c7ffe 100644
--- a/lib/mu-server.hh
+++ b/lib/mu-server.hh
@@ -24,17 +24,32 @@
#include <functional>
#include <utils/mu-sexp.hh>
+#include <utils/mu-utils.hh>
#include <mu-store.hh>
namespace Mu {
/**
- * @brief Implements the mu server, as used by mu4e.
+ * @brief Implements the mu server, as used by mu4e.
*
*/
class Server {
public:
- using Output = std::function<void(Sexp&& sexp, bool flush)>;
+ enum struct OutputFlags {
+ None = 0,
+ SplitList = 1 << 0,
+ /**< insert newlines between list items */
+ Flush = 1 << 1,
+ /**< flush output buffer after */
+ };
+
+ /**
+ * Prototype for output function
+ *
+ * @param sexp an s-expression
+ * @param flags flags that influence the behavior
+ */
+ using Output = std::function<void(Sexp&& sexp, OutputFlags flags)>;
/**
* Construct a new server
@@ -63,6 +78,9 @@ private:
struct Private;
std::unique_ptr<Private> priv_;
};
+MU_ENABLE_BITOPS(Server::OutputFlags);
+
} // namespace Mu
+
#endif /* MU_SERVER_HH__ */
diff --git a/lib/utils/mu-sexp.cc b/lib/utils/mu-sexp.cc
index 69ab9c3..ef8182e 100644
--- a/lib/utils/mu-sexp.cc
+++ b/lib/utils/mu-sexp.cc
@@ -188,6 +188,9 @@ Sexp::to_sexp_string() const
first = false;
}
sstrm << ')';
+
+ if (any_of(formatting_opts & FormattingOptions::SplitList))
+ sstrm << '\n';
break;
}
case Type::String:
@@ -225,6 +228,8 @@ Sexp::to_json_string() const
first = false;
}
sstrm << "}";
+ if (any_of(formatting_opts & FormattingOptions::SplitList))
+ sstrm << '\n';
} else { // other lists become arrays.
sstrm << '[';
bool first{true};
@@ -233,6 +238,8 @@ Sexp::to_json_string() const
first = false;
}
sstrm << ']';
+ if (any_of(formatting_opts & FormattingOptions::SplitList))
+ sstrm << '\n';
}
break;
}
diff --git a/lib/utils/mu-sexp.hh b/lib/utils/mu-sexp.hh
index 4a1e8b6..725a37b 100644
--- a/lib/utils/mu-sexp.hh
+++ b/lib/utils/mu-sexp.hh
@@ -342,6 +342,14 @@ struct Sexp {
return is_prop_list(list().begin() + 1, list().end());
}
+ enum struct FormattingOptions {
+ Default = 0, /**< Nothing in particular */
+ SplitList = 1 << 0, /**< Insert newline after list item */
+ };
+
+ FormattingOptions formatting_opts{}; /**< Formatting option for the
+ * string output */
+
private:
Sexp(Type typearg, std::string&& valuearg) : type_{typearg}, value_{std::move(valuearg)} {
if (is_list())
@@ -395,11 +403,21 @@ static inline std::ostream&
operator<<(std::ostream& os, Sexp::Type id)
{
switch (id) {
- case Sexp::Type::List: os << "list"; break;
- case Sexp::Type::String: os << "string"; break;
- case Sexp::Type::Number: os << "number"; break;
- case Sexp::Type::Symbol: os << "symbol"; break;
- case Sexp::Type::Empty: os << "empty"; break;
+ case Sexp::Type::List:
+ os << "list";
+ break;
+ case Sexp::Type::String:
+ os << "string";
+ break;
+ case Sexp::Type::Number:
+ os << "number";
+ break;
+ case Sexp::Type::Symbol:
+ os << "symbol";
+ break;
+ case Sexp::Type::Empty:
+ os << "empty";
+ break;
default: throw std::runtime_error("unknown node type");
}
@@ -419,6 +437,7 @@ operator<<(std::ostream& os, const Sexp::List& sexp)
os << Sexp::make_list(Sexp::List(sexp));
return os;
}
+MU_ENABLE_BITOPS(Sexp::FormattingOptions);
} // namespace Mu
diff --git a/mu/mu-cmd-server.cc b/mu/mu-cmd-server.cc
index 85fd06a..54daea1 100644
--- a/mu/mu-cmd-server.cc
+++ b/mu/mu-cmd-server.cc
@@ -59,7 +59,7 @@ install_sig_handler(void)
for (i = 0; i != G_N_ELEMENTS(sigs); ++i)
if (sigaction(sigs[i], &action, NULL) != 0)
g_critical("set sigaction for %d failed: %s",
- sigs[i], g_strerror(errno));
+ sigs[i], g_strerror(errno));
}
/*
@@ -82,16 +82,22 @@ cookie(size_t n)
}
static void
-output_sexp_stdout(Sexp&& sexp, bool flush = false)
+output_sexp_stdout(Sexp&& sexp, Server::OutputFlags flags)
{
+ /* if requested, insert \n between list elements; note:
+ * is _not_ inherited by children */
+ if (any_of(flags & Server::OutputFlags::SplitList))
+ sexp.formatting_opts |= Sexp::FormattingOptions::SplitList;
+
const auto str{sexp.to_sexp_string()};
+
cookie(str.size() + 1);
if (G_UNLIKELY(::puts(str.c_str()) < 0)) {
g_critical("failed to write output '%s'", str.c_str());
::raise(SIGTERM); /* terminate ourselves */
}
- if (flush)
+ if (any_of(flags & Server::OutputFlags::Flush))
std::fflush(stdout);
}
@@ -103,7 +109,8 @@ report_error(const Mu::Error& err) noexcept
e.add_prop(":error", Sexp::make_number(static_cast<size_t>(err.code())));
e.add_prop(":message", Sexp::make_string(err.what()));
- output_sexp_stdout(Sexp::make_list(std::move(e)), true /*flush*/);
+ output_sexp_stdout(Sexp::make_list(std::move(e)),
+ Server::OutputFlags::Flush);
}
MuError
@@ -113,15 +120,15 @@ try {
Server server{store, output_sexp_stdout};
g_message("created server with store @ %s; maildir @ %s; debug-mode %s",
- store.properties().database_path.c_str(),
- store.properties().root_maildir.c_str(),
- opts->debug ? "yes" : "no");
+ store.properties().database_path.c_str(),
+ store.properties().root_maildir.c_str(),
+ opts->debug ? "yes" : "no");
tty = ::isatty(::fileno(stdout));
const auto eval = std::string{opts->commands ? "(help :full t)"
- : opts->eval ? opts->eval
- : ""};
+ : opts->eval ? opts->eval
+ : ""};
if (!eval.empty()) {
server.invoke(eval);
return MU_OK;