commit 8480869: [Feature] Allow to sort symbols output

Vsevolod Stakhov vsevolod at rspamd.com
Wed Jun 8 22:00:03 UTC 2022


Author: Vsevolod Stakhov
Date: 2022-06-08 22:46:40 +0100
URL: https://github.com/rspamd/rspamd/commit/8480869c628869ecde69d3f7ddd3d1434b50f784

[Feature] Allow to sort symbols output

---
 doc/rspamc.1.md       |   2 +-
 src/client/rspamc.cxx | 180 ++++++++++++++++++++++++++++----------------------
 2 files changed, 101 insertions(+), 81 deletions(-)

diff --git a/doc/rspamc.1.md b/doc/rspamc.1.md
index 11e2f461c..afb256792 100644
--- a/doc/rspamc.1.md
+++ b/doc/rspamc.1.md
@@ -114,7 +114,7 @@ requires input.
 :	Add custom HTTP header for a request. You may specify header in format `name=value` or just `name` for an empty header. This option can be repeated multiple times.
 
 \--sort=*type*
-:	Sort output according to a specific field. For `counters` command the allowed values for this key are `name`, `weight`, `frequency` and `hits`. Appending `:desc` to any of these types inverts sorting order.
+:	Sort output according to a specific field. For `counters` command the allowed values for this key are `name`, `weight`, `frequency` and `hits`. Appending `:asc` to any of these types inverts sorting order.
 
 \--commands
 :	List available commands
diff --git a/src/client/rspamc.cxx b/src/client/rspamc.cxx
index bdcbb0d2d..57b57e6fa 100644
--- a/src/client/rspamc.cxx
+++ b/src/client/rspamc.cxx
@@ -28,6 +28,7 @@
 #include <functional>
 #include <cstdint>
 #include <cstdio>
+#include <cmath>
 
 #include "frozen/string.h"
 #include "frozen/unordered_map.h"
@@ -353,6 +354,103 @@ static auto emphasis_argument(const T &arg, const char *fmt_string = "{}") -> au
 	return fmt::format(fmt_string, arg);
 }
 
+using sort_lambda = std::function<int(const ucl_object_t *, const ucl_object_t *)>;
+static const auto sort_map = frozen::make_unordered_map<frozen::string, sort_lambda>({
+		{"name", [](const ucl_object_t *o1, const ucl_object_t *o2) -> int {
+			const auto *elt1 = ucl_object_lookup(o1, "symbol");
+			const auto *elt2 = ucl_object_lookup(o2, "symbol");
+
+			if (elt1 && elt2) {
+				return strcmp(ucl_object_tostring(elt1),
+						ucl_object_tostring(elt2));
+			}
+			else if (ucl_object_key(o1) != nullptr && ucl_object_key(o2) != nullptr) {
+				return strcmp(ucl_object_key(o1),
+						ucl_object_key(o2));
+			}
+			return 0;
+		}},
+		{"weight", [](const ucl_object_t *o1, const ucl_object_t *o2) -> int {
+			const auto *elt1 = ucl_object_lookup(o1, "weight");
+			const auto *elt2 = ucl_object_lookup(o2, "weight");
+
+			if (elt1 && elt2) {
+				return ucl_object_todouble(elt2) * 1000.0 - ucl_object_todouble(elt1) * 1000.0;
+			}
+			return 0;
+		}},
+		{"score", [](const ucl_object_t *o1, const ucl_object_t *o2) -> int {
+			const auto *elt1 = ucl_object_lookup(o1, "score");
+			const auto *elt2 = ucl_object_lookup(o2, "score");
+
+			if (elt1 && elt2) {
+				return std::fabs(ucl_object_todouble(elt2)) * 1000.0 -
+					std::fabs(ucl_object_todouble(elt1)) * 1000.0;
+			}
+			return 0;
+		}},
+		{"time", [](const ucl_object_t *o1, const ucl_object_t *o2) -> int {
+			const auto *elt1 = ucl_object_lookup(o1, "time");
+			const auto *elt2 = ucl_object_lookup(o2, "time");
+
+			if (elt1 && elt2) {
+				return ucl_object_todouble(elt2) * 1000.0 - ucl_object_todouble(elt1) * 1000.0;
+			}
+			return 0;
+		}},
+		{"frequency", [](const ucl_object_t *o1, const ucl_object_t *o2) -> int {
+			const auto *elt1 = ucl_object_lookup(o1, "frequency");
+			const auto *elt2 = ucl_object_lookup(o2, "frequency");
+
+			if (elt1 && elt2) {
+				return ucl_object_todouble(elt2) * 1000.0 - ucl_object_todouble(elt1) * 1000.0;
+			}
+			return 0;
+		}},
+		{"hits", [](const ucl_object_t *o1, const ucl_object_t *o2) -> int {
+			const auto *elt1 = ucl_object_lookup(o1, "hits");
+			const auto *elt2 = ucl_object_lookup(o2, "hits");
+
+			if (elt1 && elt2) {
+				return ucl_object_toint(elt2) - ucl_object_toint(elt1);
+			}
+			return 0;
+		}},
+});
+
+/* TODO: remove once migrate to C++20 standard */
+static constexpr auto
+sv_ends_with(std::string_view inp, std::string_view suffix) -> bool {
+	return inp.size() >= suffix.size() && inp.compare(inp.size() - suffix.size(), std::string_view::npos, suffix) == 0;
+}
+
+template<typename T>
+auto sort_containert_with_default(T &cont, const char *default_sort,
+								  typename std::enable_if<std::is_same_v<typename T::value_type, const ucl_object_t *>>::type* = 0) -> void
+{
+	auto real_sort = sort ? sort : default_sort;
+	if (real_sort) {
+		auto sort_view = std::string_view{real_sort};
+		auto inverse = false;
+
+		if (sv_ends_with(sort_view, ":asc")) {
+			inverse = true;
+			sort_view = std::string_view{sort, strlen(sort) - sizeof(":asc") + 1};
+		}
+
+		const auto sort_functor = sort_map.find(sort_view);
+		if (sort_functor != sort_map.end()) {
+			std::stable_sort(std::begin(cont), std::end(cont),
+					[&](const ucl_object_t *o1, const ucl_object_t *o2) -> int {
+						auto order = sort_functor->second(o1, o2);
+
+						return inverse ? order > 0 : order < 0;
+					});
+		}
+	}
+}
+
+
 static gboolean
 rspamc_password_callback(const gchar *option_name,
 						 const gchar *value,
@@ -813,10 +911,7 @@ rspamc_metric_output(FILE *out, const ucl_object_t *obj)
 			symbols.push_back(cur);
 		}
 
-		std::stable_sort(std::begin(symbols), std::end(symbols),
-						 [](const ucl_object_t *u1, const ucl_object_t *u2) -> int {
-							 return strcmp(ucl_object_key(u1), ucl_object_key(u2));
-						 });
+		sort_containert_with_default(symbols, "name");
 
 		for (const auto *sym_obj : symbols) {
 			rspamc_symbol_output(out, sym_obj);
@@ -966,65 +1061,9 @@ rspamc_uptime_output(FILE *out, ucl_object_t *obj)
 	}
 }
 
-static constexpr auto
-sv_ends_with(std::string_view inp, std::string_view suffix) -> bool {
-	return inp.size() >= suffix.size() && inp.compare(inp.size() - suffix.size(), std::string_view::npos, suffix) == 0;
-}
-
 static void
 rspamc_counters_output(FILE *out, ucl_object_t *obj)
 {
-	using sort_lambda = std::function<int(const ucl_object_t *, const ucl_object_t *)>;
-	static const auto sort_map = frozen::make_unordered_map<frozen::string, sort_lambda>({
-			{"name", [](const ucl_object_t *o1, const ucl_object_t *o2) -> int {
-				const auto *elt1 = ucl_object_lookup(o1, "symbol");
-				const auto *elt2 = ucl_object_lookup(o2, "symbol");
-
-				if (elt1 && elt2) {
-					return strcmp(ucl_object_tostring(elt1),
-							ucl_object_tostring(elt2));
-				}
-				return 0;
-			}},
-			{"weight", [](const ucl_object_t *o1, const ucl_object_t *o2) -> int {
-				const auto *elt1 = ucl_object_lookup(o1, "weight");
-				const auto *elt2 = ucl_object_lookup(o2, "weight");
-
-				if (elt1 && elt2) {
-					return ucl_object_todouble(elt1) * 1000.0 - ucl_object_todouble(elt2) * 1000.0;
-				}
-				return 0;
-			}},
-			{"time", [](const ucl_object_t *o1, const ucl_object_t *o2) -> int {
-				const auto *elt1 = ucl_object_lookup(o1, "time");
-				const auto *elt2 = ucl_object_lookup(o2, "time");
-
-				if (elt1 && elt2) {
-					return ucl_object_todouble(elt1) * 1000.0 - ucl_object_todouble(elt2) * 1000.0;
-				}
-				return 0;
-			}},
-			{"frequency", [](const ucl_object_t *o1, const ucl_object_t *o2) -> int {
-				const auto *elt1 = ucl_object_lookup(o1, "frequency");
-				const auto *elt2 = ucl_object_lookup(o2, "frequency");
-
-				if (elt1 && elt2) {
-					return ucl_object_todouble(elt1) * 1000.0 - ucl_object_todouble(elt2) * 1000.0;
-				}
-				return 0;
-			}},
-			{"hits", [](const ucl_object_t *o1, const ucl_object_t *o2) -> int {
-				const auto *elt1 = ucl_object_lookup(o1, "hits");
-				const auto *elt2 = ucl_object_lookup(o2, "hits");
-
-				if (elt1 && elt2) {
-					return ucl_object_toint(elt1) - ucl_object_toint(elt2);
-				}
-				return 0;
-			}},
-	});
-
-
 	if (obj->type != UCL_ARRAY) {
 		fmt::print(out, "Bad output\n");
 		return;
@@ -1045,26 +1084,7 @@ rspamc_counters_output(FILE *out, ucl_object_t *obj)
 		counters_vec.push_back(cur);
 	}
 
-	/* Sort symbols by their order */
-	if (sort != nullptr) {
-		auto sort_view = std::string_view{sort};
-		auto inverse = false;
-
-		if (sv_ends_with(sort_view, ":desc")) {
-			inverse = true;
-			sort_view = std::string_view{sort, strlen(sort) - sizeof(":desc") + 1};
-		}
-
-		const auto sort_functor = sort_map.find(sort_view);
-		if (sort_functor != sort_map.end()) {
-			std::stable_sort(std::begin(counters_vec), std::end(counters_vec),
-							 [&](const ucl_object_t *o1, const ucl_object_t *o2) {
-				auto order = sort_functor->second(o1, o2);
-
-				return inverse ? -(order) : order;
-			});
-		}
-	}
+	sort_containert_with_default(counters_vec, "name");
 
 	char fmt_buf[64], dash_buf[82], sym_buf[82];
 	const int dashes = 44;


More information about the Commits mailing list