mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-03-04 03:03:51 +01:00
common/json_filter: routines for json filtering.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
15112ae87b
commit
3c75770586
6 changed files with 242 additions and 0 deletions
|
@ -48,6 +48,7 @@ COMMON_SRC_NOGEN := \
|
|||
common/initial_channel.c \
|
||||
common/initial_commit_tx.c \
|
||||
common/iso4217.c \
|
||||
common/json_filter.c \
|
||||
common/json_param.c \
|
||||
common/json_parse.c \
|
||||
common/json_parse_simple.c \
|
||||
|
|
192
common/json_filter.c
Normal file
192
common/json_filter.c
Normal 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
34
common/json_filter.h
Normal 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 */
|
|
@ -4,6 +4,7 @@
|
|||
#include <assert.h>
|
||||
#include <ccan/tal/str/str.h>
|
||||
#include <common/channel_type.h>
|
||||
#include <common/json_filter.h>
|
||||
#include <common/setup.h>
|
||||
#include <inttypes.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,
|
||||
const u64 *extra_types UNNEEDED, size_t *err_off UNNEEDED, u64 *err_type UNNEEDED)
|
||||
{ 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 */
|
||||
void towire_tlv(u8 **pptr UNNEEDED,
|
||||
const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "config.h"
|
||||
#include "../../common/json_filter.c"
|
||||
#include "../../common/json_stream.c"
|
||||
#include "../jsonrpc.c"
|
||||
#include "../feerate.c"
|
||||
|
|
|
@ -140,6 +140,7 @@ PLUGIN_COMMON_OBJS := \
|
|||
common/json_param.o \
|
||||
common/json_parse.o \
|
||||
common/json_parse_simple.o \
|
||||
common/json_filter.o \
|
||||
common/json_stream.o \
|
||||
common/lease_rates.o \
|
||||
common/memleak.o \
|
||||
|
|
Loading…
Add table
Reference in a new issue