common/json_filter: routines for json filtering.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2022-11-02 10:02:22 +10:30
parent 15112ae87b
commit 3c75770586
6 changed files with 242 additions and 0 deletions

View file

@ -48,6 +48,7 @@ COMMON_SRC_NOGEN := \
common/initial_channel.c \ common/initial_channel.c \
common/initial_commit_tx.c \ common/initial_commit_tx.c \
common/iso4217.c \ common/iso4217.c \
common/json_filter.c \
common/json_param.c \ common/json_param.c \
common/json_parse.c \ common/json_parse.c \
common/json_parse_simple.c \ common/json_parse_simple.c \

192
common/json_filter.c Normal file
View file

@ -0,0 +1,192 @@
#include "config.h"
#include <assert.h>
#include <ccan/strmap/strmap.h>
#include <ccan/tal/str/str.h>
#include <common/json_filter.h>
#include <common/utils.h>
/* If they set a filter, we keep it in a tree. */
struct json_filter {
/* We accumulate errors: if they treat an array as an object */
bool misused;
/* Pointer to parent, or NULL at top. */
struct json_filter *parent;
/* Tracks how far we are into filter, e.g.
* if they specify "peers.foo" and we're
* in "peer.foo.bar" depth will be 1. */
size_t depth;
/* If we're in "peer.bar", we're negative */
bool positive;
/* If this is an array */
struct json_filter *filter_array;
/* Otherwise, object: one per keyword */
STRMAP(struct json_filter *) filter_map;
};
/* Returns true if we should print this member: this is a shortcut for:
*
* json_filter_down(filter, member);
* ret = json_filter_ok(filter, NULL);
* json_filter_up(filter);
*
*/
bool json_filter_ok(const struct json_filter *filter, const char *member)
{
if (!filter)
return true;
if (filter->depth > 0 || !member)
return filter->positive;
return strmap_get(&filter->filter_map, member) != NULL;
}
/* Returns true if we should print this new obj/array */
bool json_filter_down(struct json_filter **filter, const char *member)
{
struct json_filter *child;
if (!*filter)
return true;
if ((*filter)->depth > 0) {
(*filter)->depth++;
return (*filter)->positive;
}
/* If we're a leaf node: all true. */
if (!(*filter)->filter_array && strmap_empty(&(*filter)->filter_map)) {
assert((*filter)->positive);
(*filter)->depth = 1;
return true;
}
/* Array? */
if (!member) {
if (!(*filter)->filter_array) {
(*filter)->misused = true;
goto fail;
}
child = (*filter)->filter_array;
} else {
if ((*filter)->filter_array) {
(*filter)->misused = true;
goto fail;
}
child = strmap_get(&(*filter)->filter_map, member);
}
if (child) {
/* Should have been cleaned up last time. */
assert(child->depth == 0);
/* We only have positive filters natively. */
assert(child->positive == true);
*filter = child;
return true;
}
/* OK, this path wasn't specified. */
fail:
(*filter)->positive = false;
(*filter)->depth = 1;
return false;
}
/* Returns true if we were printing (i.e. close object/arr) */
bool json_filter_up(struct json_filter **filter)
{
if (!*filter)
return true;
if ((*filter)->depth == 0) {
assert((*filter)->parent);
assert((*filter)->parent->depth == 0);
/* Reset for next time */
(*filter)->positive = true;
*filter = (*filter)->parent;
return true;
}
(*filter)->depth--;
return (*filter)->positive;
}
static void destroy_json_filter(struct json_filter *filter)
{
strmap_clear(&filter->filter_map);
}
struct json_filter *json_filter_new(const tal_t *ctx)
{
struct json_filter *filter = tal(ctx, struct json_filter);
filter->misused = false;
filter->parent = NULL;
filter->depth = 0;
filter->positive = true;
filter->filter_array = NULL;
strmap_init(&filter->filter_map);
tal_add_destructor(filter, destroy_json_filter);
return filter;
}
struct json_filter *json_filter_subobj(struct json_filter *filter,
const char *fieldname,
size_t fieldnamelen)
{
struct json_filter *subfilter = json_filter_new(filter);
subfilter->parent = filter;
strmap_add(&filter->filter_map,
tal_strndup(filter, fieldname, fieldnamelen),
subfilter);
return subfilter;
}
struct json_filter *json_filter_subarr(struct json_filter *filter)
{
struct json_filter *subfilter = json_filter_new(filter);
subfilter->parent = filter;
filter->filter_array = subfilter;
return subfilter;
}
bool json_filter_finished(const struct json_filter *filter)
{
return !filter->parent && filter->depth == 0;
}
static bool strmap_filter_misused(const char *member,
struct json_filter *filter,
const char **ret)
{
*ret = json_filter_misused(tmpctx, filter);
if (*ret == NULL)
return true;
/* If there was a problem, prepend member and stop iterating */
*ret = tal_fmt(tmpctx, ".%s%s", member, *ret);
return false;
}
const char *json_filter_misused(const tal_t *ctx, const struct json_filter *f)
{
const char *ret;
if (f->misused) {
if (f->filter_array)
return tal_fmt(ctx, " is an object");
else
return tal_fmt(ctx, " is an array");
}
if (f->filter_array) {
ret = json_filter_misused(tmpctx, f->filter_array);
if (ret)
return tal_fmt(ctx, "[]%s", ret);
return NULL;
} else {
ret = NULL;
strmap_iterate(&f->filter_map, strmap_filter_misused, &ret);
return tal_steal(ctx, ret);
}
}

34
common/json_filter.h Normal file
View file

@ -0,0 +1,34 @@
/*
* Helpers for filtering JSON results while generating.
*/
#ifndef LIGHTNING_COMMON_JSON_FILTER_H
#define LIGHTNING_COMMON_JSON_FILTER_H
#include "config.h"
#include <ccan/tal/tal.h>
#include <stdbool.h>
struct json_filter;
/* Print this? */
bool json_filter_ok(const struct json_filter *filter, const char *member);
/* Returns true if we should print this new obj/array */
bool json_filter_down(struct json_filter **filter, const char *member);
/* Returns true if we were printing (i.e. close object/arr) */
bool json_filter_up(struct json_filter **filter);
/* Is filter finished (i.e. balanced!) */
bool json_filter_finished(const struct json_filter *filter);
/* Has filter been misused? If so, returns explanatory string, otherwise NULL */
const char *json_filter_misused(const tal_t *ctx, const struct json_filter *f);
/* Filter allocation */
struct json_filter *json_filter_new(const tal_t *ctx);
struct json_filter *json_filter_subobj(struct json_filter *filter,
const char *fieldname,
size_t fieldnamelen);
struct json_filter *json_filter_subarr(struct json_filter *filter);
#endif /* LIGHTNING_COMMON_JSON_FILTER_H */

View file

@ -4,6 +4,7 @@
#include <assert.h> #include <assert.h>
#include <ccan/tal/str/str.h> #include <ccan/tal/str/str.h>
#include <common/channel_type.h> #include <common/channel_type.h>
#include <common/json_filter.h>
#include <common/setup.h> #include <common/setup.h>
#include <inttypes.h> #include <inttypes.h>
#include <stdio.h> #include <stdio.h>
@ -17,6 +18,18 @@ bool fromwire_tlv(const u8 **cursor UNNEEDED, size_t *max UNNEEDED,
void *record UNNEEDED, struct tlv_field **fields UNNEEDED, void *record UNNEEDED, struct tlv_field **fields UNNEEDED,
const u64 *extra_types UNNEEDED, size_t *err_off UNNEEDED, u64 *err_type UNNEEDED) const u64 *extra_types UNNEEDED, size_t *err_off UNNEEDED, u64 *err_type UNNEEDED)
{ fprintf(stderr, "fromwire_tlv called!\n"); abort(); } { fprintf(stderr, "fromwire_tlv called!\n"); abort(); }
/* Generated stub for json_filter_down */
bool json_filter_down(struct json_filter **filter UNNEEDED, const char *member UNNEEDED)
{ fprintf(stderr, "json_filter_down called!\n"); abort(); }
/* Generated stub for json_filter_finished */
bool json_filter_finished(const struct json_filter *filter UNNEEDED)
{ fprintf(stderr, "json_filter_finished called!\n"); abort(); }
/* Generated stub for json_filter_ok */
bool json_filter_ok(const struct json_filter *filter UNNEEDED, const char *member UNNEEDED)
{ fprintf(stderr, "json_filter_ok called!\n"); abort(); }
/* Generated stub for json_filter_up */
bool json_filter_up(struct json_filter **filter UNNEEDED)
{ fprintf(stderr, "json_filter_up called!\n"); abort(); }
/* Generated stub for towire_tlv */ /* Generated stub for towire_tlv */
void towire_tlv(u8 **pptr UNNEEDED, void towire_tlv(u8 **pptr UNNEEDED,
const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED,

View file

@ -1,4 +1,5 @@
#include "config.h" #include "config.h"
#include "../../common/json_filter.c"
#include "../../common/json_stream.c" #include "../../common/json_stream.c"
#include "../jsonrpc.c" #include "../jsonrpc.c"
#include "../feerate.c" #include "../feerate.c"

View file

@ -140,6 +140,7 @@ PLUGIN_COMMON_OBJS := \
common/json_param.o \ common/json_param.o \
common/json_parse.o \ common/json_parse.o \
common/json_parse_simple.o \ common/json_parse_simple.o \
common/json_filter.o \
common/json_stream.o \ common/json_stream.o \
common/lease_rates.o \ common/lease_rates.o \
common/memleak.o \ common/memleak.o \