mu: support json output directly

Allow for dumping json directly from the Sexp structures, so we don't
need any external libs (i.e. json-glib) anymore.
This commit is contained in:
Dirk-Jan C. Binnema 2020-10-26 18:39:56 +02:00
parent f2e87ea2d4
commit d2aa1f91b0
10 changed files with 93 additions and 66 deletions

View File

@ -146,14 +146,6 @@ glib_version="$($PKG_CONFIG --modversion glib-2.0)"
PKG_CHECK_MODULES(GMIME,gmime-3.0)
gmime_version="$($PKG_CONFIG --modversion gmime-3.0)"
# gmime, version 3.0 or higher
PKG_CHECK_MODULES(JSON_GLIB,json-glib-1.0 >= 1.4,[have_json_glib=yes],[have_json_glib=no])
AS_IF([test "x$have_json_glib" = "xyes"],[
json_glib_version="$($PKG_CONFIG --modversion json-glib-1.0)"
AC_DEFINE(HAVE_JSON_GLIB,[1], [Do we support json-glib?])
])
AM_CONDITIONAL(HAVE_JSON_GLIB,[test "x$have_json_glib" = "xyes"])
# xapian checking - we need 1.4 at least
PKG_CHECK_MODULES(XAPIAN,xapian-core >= 1.4,[
have_xapian=yes
@ -296,10 +288,6 @@ echo "Xapian version : $xapian_version"
echo "GLib version : $glib_version"
echo "GMime version : $gmime_version"
AM_COND_IF([HAVE_JSON_GLIB],[
echo "Json-Glib version : $json_glib_version"
])
AM_COND_IF([BUILD_GUI],[
echo "GTK+ version : $gtk_version"
echo "Webkit2/GTK+ version : $webkit_version"

View File

@ -20,13 +20,6 @@ include $(top_srcdir)/gtest.mk
SUBDIRS= utils query index
if HAVE_JSON_GLIB
json_srcs= \
mu-msg-json.c \
mu-msg-json.h
json_flag="-DHAVE_JSON_GLIB"
endif
TESTDEFS= \
-DMU_TESTMAILDIR=\"${abs_srcdir}/testdir\" \
-DMU_TESTMAILDIR2=\"${abs_srcdir}/testdir2\" \

View File

@ -478,5 +478,14 @@ mu_msg_to_sexp (MuMsg *msg, unsigned docid, const MuMsgIterThreadInfo *ti,
MuMsgOptions opts)
{
return g_strdup (Mu::msg_to_sexp (msg, docid, ti, opts)
.to_string().c_str());
.to_sexp_string().c_str());
}
char*
mu_msg_to_json (MuMsg *msg, unsigned docid, const MuMsgIterThreadInfo *ti,
MuMsgOptions opts)
{
return g_strdup (Mu::msg_to_sexp (msg, docid, ti, opts)
.to_json_string().c_str());
}

View File

@ -595,10 +595,6 @@ char* mu_str_flags (MuFlags flags)
struct _MuMsgIterThreadInfo;
#ifdef HAVE_JSON_GLIB
struct _JsonNode; /* forward declaration */
/**
* convert the msg to json
*
@ -612,13 +608,11 @@ struct _JsonNode; /* forward declaration */
* - MU_MSG_OPTION_EXTRACT_IMAGES: extract image attachments as temporary
* files and include links to those in the sexp
*
* @return a string with the sexp (free with g_free) or NULL in case of error
* @return a string with the json (free with g_free) or NULL in case of error
*/
struct _JsonNode* mu_msg_to_json (MuMsg *msg, unsigned docid,
char* mu_msg_to_json (MuMsg *msg, unsigned docid,
const struct _MuMsgIterThreadInfo *ti,
MuMsgOptions ops) G_GNUC_WARN_UNUSED_RESULT;
#endif /*HAVE_JSON_GLIB*/
/**
* convert the msg to a Lisp symbolic expression (for further processing in

View File

@ -33,7 +33,7 @@ Command::invoke(const Command::CommandMap& cmap, const Sexp& call)
if (!call.is_call()) {
throw Mu::Error{Error::Code::Command,
"expected call-sexpr but got %s",
call.to_string().c_str()};
call.to_sexp_string().c_str()};
}
const auto& params{call.list()};
@ -41,7 +41,7 @@ Command::invoke(const Command::CommandMap& cmap, const Sexp& call)
if (cmd_it == cmap.end())
throw Mu::Error{Error::Code::Command,
"unknown command in call %s",
call.to_string().c_str()};
call.to_sexp_string().c_str()};
const auto& cinfo{cmd_it->second};
@ -67,7 +67,7 @@ Command::invoke(const Command::CommandMap& cmap, const Sexp& call)
if (arginfo.required)
throw Mu::Error{Error::Code::Command,
"missing required parameter %s in call %s",
argname.c_str(), call.to_string().c_str()};
argname.c_str(), call.to_sexp_string().c_str()};
continue; // not required
}
@ -78,7 +78,7 @@ Command::invoke(const Command::CommandMap& cmap, const Sexp& call)
"parameter %s expects type %s, but got %s in call %s",
argname.c_str(), to_string(arginfo.type).c_str(),
to_string(param_it->type()).c_str(),
call.to_string().c_str()};
call.to_sexp_string().c_str()};
}
// all passed parameters must be known
@ -87,7 +87,7 @@ Command::invoke(const Command::CommandMap& cmap, const Sexp& call)
[&](auto&& arg) {return params.at(i).value() == arg.first;}))
throw Mu::Error{Error::Code::Command,
"unknown parameter %s in call %s",
params.at(i).value().c_str(), call.to_string().c_str()};
params.at(i).value().c_str(), call.to_sexp_string().c_str()};
}
if (cinfo.handler)

View File

@ -179,7 +179,7 @@ Sexp::make_parse (const std::string& expr)
std::string
Sexp::to_string () const
Sexp::to_sexp_string () const
{
std::stringstream sstrm;
@ -188,7 +188,7 @@ Sexp::to_string () const
sstrm << '(';
bool first{true};
for (auto&& child : list()) {
sstrm << (first ? "" : " ") << child.to_string();
sstrm << (first ? "" : " ") << child.to_sexp_string();
first = false;
}
sstrm << ')';
@ -206,3 +206,55 @@ Sexp::to_string () const
return sstrm.str();
}
std::string
Sexp::to_json_string () const
{
std::stringstream sstrm;
switch (type()) {
case Type::List: {
// property-lists become JSON objects
if (is_prop_list()) {
sstrm << "{";
auto it{list().begin()};
bool first{true};
while (it != list().end()) {
sstrm << (first?"":",") << quote(it->value()) << ":";
++it;
sstrm << it->to_json_string();
++it;
first = false;
}
sstrm << "}";
} else { // other lists become arrays.
sstrm << '[';
bool first{true};
for (auto&& child : list()) {
sstrm << (first ? "" : ", ") << child.to_json_string();
first = false;
}
sstrm << ']';
}
break;
}
case Type::String:
sstrm << quote(value());
break;
case Type::Symbol:
if (is_nil())
sstrm << "false";
else if (is_t())
sstrm << "true";
else
sstrm << quote(value());
break;
case Type::Number:
case Type::Empty:
default:
sstrm << value();
}
return sstrm.str();
}

View File

@ -104,11 +104,19 @@ struct Sexp {
}
/**
* Convert a Sexp::Node to its string representation
* Convert a Sexp::Node to its S-expression string representation
*
* @return the string representation
*/
std::string to_string() const;
std::string to_sexp_string() const;
/**
* Convert a Sexp::Node to its JSON string representation
*
* @return the string representation
*/
std::string to_json_string() const;
/**
* Return the type of this Node.
@ -341,7 +349,7 @@ operator<<(std::ostream& os, Sexp::Type id)
static inline std::ostream&
operator<<(std::ostream& os, const Sexp& sexp)
{
os << sexp.to_string();
os << sexp.to_sexp_string();
return os;
}

View File

@ -63,17 +63,17 @@ test_list()
const auto nstr{Sexp::make_string("foo")};
g_assert_true(nstr.value() == "foo");
g_assert_true(nstr.type() == Sexp::Type::String);
assert_equal(nstr.to_string(), "\"foo\"");
assert_equal(nstr.to_sexp_string(), "\"foo\"");
const auto nnum{Sexp::make_number(123)};
g_assert_true(nnum.value() == "123");
g_assert_true(nnum.type() == Sexp::Type::Number);
assert_equal(nnum.to_string(), "123");
assert_equal(nnum.to_sexp_string(), "123");
const auto nsym{Sexp::make_symbol("blub")};
g_assert_true(nsym.value() == "blub");
g_assert_true(nsym.type() == Sexp::Type::Symbol);
assert_equal(nsym.to_string(), "blub");
assert_equal(nsym.to_sexp_string(), "blub");
Sexp::List list;
list .add(Sexp::make_string("foo"))
@ -85,7 +85,7 @@ test_list()
g_assert_true(nlst.type() == Sexp::Type::List);
g_assert_true(nlst.list().at(1).value() == "123");
assert_equal(nlst.to_string(),"(\"foo\" 123 blub)");
assert_equal(nlst.to_sexp_string(),"(\"foo\" 123 blub)");
}
@ -95,7 +95,7 @@ test_prop_list()
Sexp::List l1;
l1.add_prop(":foo", Sexp::make_string("bar"));
Sexp s2{Sexp::make_list(std::move(l1))};
assert_equal(s2.to_string(), "(:foo \"bar\")");
assert_equal(s2.to_sexp_string(), "(:foo \"bar\")");
Sexp::List l2;
@ -105,7 +105,7 @@ test_prop_list()
Sexp::List l3;
l3.add_prop(":cuux", Sexp::make_list(std::move(l2)));
Sexp s3{Sexp::make_list(std::move(l3))};
assert_equal(s3.to_string(), "(:cuux (:foo \"bar\" :bar 77))");
assert_equal(s3.to_sexp_string(), "(:cuux (:foo \"bar\" :bar 77))");
}
static void
@ -122,7 +122,7 @@ test_props()
":flub", Sexp::make_symbol("fnord"),
":boo", std::move(sexp2));
assert_equal(sexp.to_string(),
assert_equal(sexp.to_sexp_string(),
"(:foo \"b\303\244r\" :cuux 123 :flub fnord :boo (\"foo\" 123 blub))");
}

View File

@ -42,10 +42,6 @@
#include "mu-cmd.hh"
#include "mu-threader.h"
#ifdef HAVE_JSON_GLIB
#include <json-glib/json-glib.h>
#endif /*HAVE_JSON_GLIB*/
using namespace Mu;
typedef gboolean (OutputFunc) (MuMsg *msg, MuMsgIter *iter,
@ -539,7 +535,6 @@ to_string (const Mu::Sexp& sexp, bool color, size_t level = 0)
}
static gboolean
output_sexp (MuMsg *msg, MuMsgIter *iter, const MuConfig *opts, GError **err)
{
@ -555,21 +550,15 @@ output_sexp (MuMsg *msg, MuMsgIter *iter, const MuConfig *opts, GError **err)
static gboolean
output_json (MuMsg *msg, MuMsgIter *iter, const MuConfig *opts, GError **err)
{
#ifdef HAVE_JSON_GLIB
JsonNode *node;
const MuMsgIterThreadInfo *ti;
char *s;
if (mu_msg_iter_is_first(iter))
g_print ("[\n");
ti = opts->threads ? mu_msg_iter_get_thread_info (iter) : NULL;
node = mu_msg_to_json (msg, mu_msg_iter_get_docid (iter),
ti, MU_MSG_OPTION_HEADERS_ONLY);
s = json_to_string (node, TRUE);
json_node_free (node);
ti = opts->threads ? mu_msg_iter_get_thread_info (iter) : NULL;
s = mu_msg_to_json (msg, mu_msg_iter_get_docid (iter),
ti, MU_MSG_OPTION_HEADERS_ONLY);
fputs (s, stdout);
g_free (s);
@ -579,12 +568,6 @@ output_json (MuMsg *msg, MuMsgIter *iter, const MuConfig *opts, GError **err)
fputs (",\n", stdout);
return TRUE;
#else
g_set_error (err, MU_ERROR_DOMAIN, MU_ERROR_IN_PARAMETERS,
"this mu was built without json support");
return FALSE;
#endif /*HAVE_JSON_GLIB*/
}
static void

View File

@ -147,7 +147,7 @@ print_expr (const char* frm, ...)
static void
print_expr (const Sexp& sexp)
{
print_expr ("%s", sexp.to_string().c_str());
print_expr ("%s", sexp.to_sexp_string().c_str());
}
static void