commit b4cce29eceb379927c76262bc7cf0eecea45d6a0
parent cb1e000fac4bf5830ab255e839a8787815fa796b
Author: William Casarin <jb55@jb55.com>
Date: Sat, 3 Aug 2024 13:34:36 -0700
add ndb_filter_json method
Changelog-Added: Add ndb_filter_json method for creating json filters
Signed-off-by: William Casarin <jb55@jb55.com>
Diffstat:
M | src/nostrdb.c | | | 145 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- |
M | src/nostrdb.h | | | 2 | ++ |
M | test.c | | | 45 | +++++++++++++++++++++++++++++++++++++++++++++ |
3 files changed, 190 insertions(+), 2 deletions(-)
diff --git a/src/nostrdb.c b/src/nostrdb.c
@@ -649,6 +649,12 @@ ndb_filter_get_string_element(struct ndb_filter *filter, struct ndb_filter_eleme
return (const char *)ndb_filter_elements_data(filter, els->elements[index]);
}
+static inline uint64_t
+ndb_filter_get_int_element(struct ndb_filter_elements *els, int index)
+{
+ return els->elements[index];
+}
+
int ndb_filter_init(struct ndb_filter *filter)
{
struct cursor cur;
@@ -793,6 +799,8 @@ static int ndb_filter_add_element(struct ndb_filter *filter, union ndb_filter_el
if (!cursor_push_c_str(&filter->data_buf, el.string))
return 0;
break;
+ case NDB_ELEMENT_INT:
+ // ints are not allowed in tag filters
case NDB_ELEMENT_UNKNOWN:
return 0;
}
@@ -879,6 +887,9 @@ int ndb_filter_add_int_element(struct ndb_filter *filter, uint64_t integer)
break;
}
+ if (!ndb_filter_set_elem_type(filter, NDB_ELEMENT_INT))
+ return 0;
+
el.integer = integer;
return ndb_filter_add_element(filter, el);
@@ -978,6 +989,8 @@ static int ndb_tag_filter_matches(struct ndb_filter *filter,
if (!strcmp(el_str, str.str))
return 1;
break;
+ case NDB_ELEMENT_INT:
+ // int elements int tag queries are not supported
case NDB_ELEMENT_UNKNOWN:
return 0;
}
@@ -4753,10 +4766,10 @@ static int cursor_push_hex(struct cursor *c, unsigned char *bytes, int len)
return 1;
}
-static int cursor_push_int_str(struct cursor *c, int num)
+static int cursor_push_int_str(struct cursor *c, uint64_t num)
{
char timebuf[16] = {0};
- snprintf(timebuf, sizeof(timebuf), "%d", num);
+ snprintf(timebuf, sizeof(timebuf), "%" PRIu64, num);
return cursor_push_str(c, timebuf);
}
@@ -4789,6 +4802,134 @@ int ndb_note_json(struct ndb_note *note, char *buf, int buflen)
return cur.p - cur.start;
}
+static int cursor_push_json_elem_array(struct cursor *cur,
+ struct ndb_filter *filter,
+ struct ndb_filter_elements *elems)
+{
+ int i;
+ unsigned char *id;
+ const char *str;
+ uint64_t val;
+
+ if (!cursor_push_byte(cur, '['))
+ return 0;
+
+ for (i = 0; i < elems->count; i++) {
+
+ switch (elems->field.elem_type) {
+ case NDB_ELEMENT_STRING:
+ str = ndb_filter_get_string_element(filter, elems, i);
+ if (!cursor_push_jsonstr(cur, str))
+ return 0;
+ break;
+ case NDB_ELEMENT_ID:
+ id = ndb_filter_get_id_element(filter, elems, i);
+ if (!cursor_push_hex_str(cur, id, 32))
+ return 0;
+ break;
+ case NDB_ELEMENT_INT:
+ val = ndb_filter_get_int_element(elems, i);
+ if (!cursor_push_int_str(cur, val))
+ return 0;
+ break;
+ case NDB_ELEMENT_UNKNOWN:
+ ndb_debug("unknown element in cursor_push_json_elem_array");
+ return 0;
+ }
+
+ if (i != elems->count-1) {
+ if (!cursor_push_byte(cur, ','))
+ return 0;
+ }
+ }
+
+ if (!cursor_push_str(cur, "]"))
+ return 0;
+
+ return 1;
+}
+
+int ndb_filter_json(struct ndb_filter *filter, char *buf, int buflen)
+{
+ struct cursor cur, *c = &cur;
+ struct ndb_filter_elements *elems;
+ int i;
+
+ if (!filter->finalized) {
+ ndb_debug("filter not finalized in ndb_filter_json\n");
+ return 0;
+ }
+
+ make_cursor((unsigned char *)buf, (unsigned char*)buf + buflen, c);
+
+ if (!cursor_push_str(c, "{"))
+ return 0;
+
+ for (i = 0; i < filter->num_elements; i++) {
+ elems = ndb_filter_get_elements(filter, i);
+ switch (elems->field.type) {
+ case NDB_FILTER_IDS:
+ if (!cursor_push_str(c, "\"ids\":"))
+ return 0;
+ if (!cursor_push_json_elem_array(c, filter, elems))
+ return 0;
+ break;
+ case NDB_FILTER_AUTHORS:
+ if (!cursor_push_str(c, "\"authors\":"))
+ return 0;
+ if (!cursor_push_json_elem_array(c, filter, elems))
+ return 0;
+ break;
+ case NDB_FILTER_KINDS:
+ if (!cursor_push_str(c, "\"kinds\":"))
+ return 0;
+ if (!cursor_push_json_elem_array(c, filter, elems))
+ return 0;
+ break;
+ case NDB_FILTER_TAGS:
+ if (!cursor_push_str(c, "\"#"))
+ return 0;
+ if (!cursor_push_byte(c, elems->field.tag))
+ return 0;
+ if (!cursor_push_str(c, "\":"))
+ return 0;
+ if (!cursor_push_json_elem_array(c, filter, elems))
+ return 0;
+ break;
+ case NDB_FILTER_SINCE:
+ if (!cursor_push_str(c, "\"since\":"))
+ return 0;
+ if (!cursor_push_int_str(c, ndb_filter_get_int_element(elems, 0)))
+ return 0;
+ break;
+ case NDB_FILTER_UNTIL:
+ if (!cursor_push_str(c, "\"until\":"))
+ return 0;
+ if (!cursor_push_int_str(c, ndb_filter_get_int_element(elems, 0)))
+ return 0;
+ break;
+ case NDB_FILTER_LIMIT:
+ if (!cursor_push_str(c, "\"limit\":"))
+ return 0;
+ if (!cursor_push_int_str(c, ndb_filter_get_int_element(elems, 0)))
+ return 0;
+ break;
+ }
+
+ if (i != filter->num_elements-1) {
+ if (!cursor_push_byte(c, ',')) {
+ return 0;
+ }
+ }
+
+ }
+
+ if (!cursor_push_c_str(c, "}"))
+ return 0;
+
+ return cur.p - cur.start;
+}
+
int ndb_calculate_id(struct ndb_note *note, unsigned char *buf, int buflen) {
int len;
diff --git a/src/nostrdb.h b/src/nostrdb.h
@@ -161,6 +161,7 @@ enum ndb_generic_element_type {
NDB_ELEMENT_UNKNOWN = 0,
NDB_ELEMENT_STRING = 1,
NDB_ELEMENT_ID = 2,
+ NDB_ELEMENT_INT = 3,
};
enum ndb_search_order {
@@ -495,6 +496,7 @@ int ndb_filter_clone(struct ndb_filter *dst, struct ndb_filter *src);
int ndb_filter_end(struct ndb_filter *);
void ndb_filter_end_field(struct ndb_filter *);
void ndb_filter_destroy(struct ndb_filter *);
+int ndb_filter_json(struct ndb_filter *, char *buf, int buflen);
// SUBSCRIPTIONS
uint64_t ndb_subscribe(struct ndb *, struct ndb_filter *, int num_filters);
diff --git a/test.c b/test.c
@@ -120,6 +120,50 @@ static void test_filters()
ndb_filter_destroy(f);
}
+static void test_filter_json()
+{
+ struct ndb_filter filter, *f = &filter;
+ char buf[1024];
+
+ unsigned char pk[32] = { 0x32, 0xe1, 0x82, 0x76, 0x35, 0x45, 0x0e, 0xbb, 0x3c, 0x5a, 0x7d, 0x12, 0xc1, 0xf8, 0xe7, 0xb2, 0xb5, 0x14, 0x43, 0x9a, 0xc1, 0x0a, 0x67, 0xee, 0xf3, 0xd9, 0xfd, 0x9c, 0x5c, 0x68, 0xe2, 0x45 };
+
+ unsigned char pk2[32] = {
+ 0xd1, 0x2c, 0x17, 0xbd, 0xe3, 0x09, 0x4a, 0xd3, 0x2f, 0x4a, 0xb8, 0x62, 0xa6, 0xcc, 0x6f, 0x5c, 0x28, 0x9c, 0xfe, 0x7d, 0x58, 0x02, 0x27, 0x0b, 0xdf, 0x34, 0x90, 0x4d, 0xf5, 0x85, 0xf3, 0x49
+ };
+
+ ndb_filter_init(f);
+ assert(ndb_filter_start_field(f, NDB_FILTER_UNTIL));
+ assert(ndb_filter_add_int_element(f, 42));
+ ndb_filter_end_field(f);
+ ndb_filter_end(f);
+ assert(ndb_filter_json(f, buf, sizeof(buf)));
+ assert(!strcmp("{\"until\":42}", buf));
+ ndb_filter_destroy(f);
+
+ ndb_filter_init(f);
+ assert(ndb_filter_start_field(f, NDB_FILTER_UNTIL));
+ assert(ndb_filter_add_int_element(f, 42));
+ ndb_filter_end_field(f);
+ assert(ndb_filter_start_field(f, NDB_FILTER_KINDS));
+ assert(ndb_filter_add_int_element(f, 1));
+ assert(ndb_filter_add_int_element(f, 2));
+ ndb_filter_end_field(f);
+ ndb_filter_end(f);
+ assert(ndb_filter_json(f, buf, sizeof(buf)));
+ assert(!strcmp("{\"until\":42,\"kinds\":[1,2]}", buf));
+ ndb_filter_destroy(f);
+
+ ndb_filter_init(f);
+ assert(ndb_filter_start_field(f, NDB_FILTER_IDS));
+ assert(ndb_filter_add_id_element(f, pk));
+ assert(ndb_filter_add_id_element(f, pk2));
+ ndb_filter_end_field(f);
+ ndb_filter_end(f);
+ assert(ndb_filter_json(f, buf, sizeof(buf)));
+ assert(!strcmp("{\"ids\":[\"32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245\",\"d12c17bde3094ad32f4ab862a6cc6f5c289cfe7d5802270bdf34904df585f349\"]}", buf));
+ ndb_filter_destroy(f);
+}
+
static void test_invoice_encoding(const char *bolt11_str)
{
unsigned char buf[4096];
@@ -1543,6 +1587,7 @@ static void test_weird_note_corruption() {
}
int main(int argc, const char *argv[]) {
+ test_filter_json();
test_bech32_parsing();
test_single_url_parsing();
test_url_parsing();