diff --git a/kore_json_construct.c b/kore_json_construct.c new file mode 100644 index 0000000..a8f7d99 --- /dev/null +++ b/kore_json_construct.c @@ -0,0 +1,238 @@ +#include + +// Simple function I wrote to make my life easier while playing with Kore C web server +// +// Should be easily portable to not use any json libraries at all if you really wanna to +// +// I know that it uses ?: GCC extension but in this file it can be expanded to standard C ternary operator with simple +// sed expression, everything for your closed-source "can't add useful features to my compiler" needs :) +// +// Usage: +// N-th variadic argument starts new context with one of the json types +// N+1 sets the name of object if current context is not an array, can be NULL +// N+2 sets the object value +// N+M marks the end with JSON_END flag if the context was object or array +// +// Example: +// root = json_construct( NULL, +// JSON_TYPE_OBJECT, NULL, +// JSON_TYPE_STRING, "version", "2.1", +// JSON_TYPE_OBJECT, "software", +// JSON_TYPE_STRING, "name", "apserv", +// JSON_TYPE_STRING, "version", "0.0.1", +// JSON_TYPE_STRING, "repository", "https://git.mentality.rip/FWGS/apserv", +// JSON_TYPE_STRING, "homepage", "https://git.mentality.rip/FWGS/apserv", +// JSON_END, +// JSON_TYPE_ARRAY, "protocols", +// JSON_TYPE_STRING, "activitypub", +// JSON_END, +// JSON_TYPE_OBJECT, "services", +// JSON_TYPE_ARRAY, "inbound", JSON_END, +// JSON_TYPE_ARRAY, "outbound", JSON_END, +// JSON_END, +// JSON_TYPE_LITERAL, "openRegistrations", APSERV_OPEN_REGISTRATIONS ? JSON_TRUE : JSON_FALSE, +// JSON_TYPE_OBJECT, "usage", +// JSON_TYPE_OBJECT, "users", +// JSON_TYPE_U64, "total", 0, +// JSON_TYPE_U64, "activeHalfyear", 0, +// JSON_TYPE_U64, "activeMonth", 0, +// JSON_END, +// JSON_END, +// JSON_TYPE_OBJECT, "metadata", +// JSON_END, +// JSON_END); +// +// This generates an object below: +// { +// "version": "2.1", +// "software": { +// "name": "apserv", +// "version": "0.0.1", +// "repository": "https://git.mentality.rip/FWGS/apserv", +// "homepage": "https://git.mentality.rip/FWGS/apserv" +// }, +// "protocols": [ +// "activitypub" +// ], +// "services": { +// "inbound": [], +// "outbound": [] +// }, +// "openRegistrations": false, +// "usage": { +// "users": { +// "total": 0, +// "activeHalfyear": 0, +// "activeMonth": 0 +// } +// }, +// "metadata": {} +// } +// + + +#include +#include +#include +#include + +#define JSON_END 0 +#define JSON_TYPE_OBJECT KORE_JSON_TYPE_OBJECT +#define JSON_TYPE_ARRAY KORE_JSON_TYPE_ARRAY +#define JSON_TYPE_STRING KORE_JSON_TYPE_STRING +#define JSON_TYPE_NUMBER KORE_JSON_TYPE_NUMBER +#define JSON_TYPE_LITERAL KORE_JSON_TYPE_LITERAL +#define JSON_TYPE_I32 (1U<<31) +#define JSON_TYPE_U32 (1U<<30) +#define JSON_TYPE_I64 KORE_JSON_TYPE_INTEGER +#define JSON_TYPE_U64 KORE_JSON_TYPE_INTEGER_U64 + +#define JSON_FALSE KORE_JSON_FALSE +#define JSON_TRUE KORE_JSON_TRUE +#define JSON_NULL KORE_JSON_NULL + +struct kore_json_item *json_construct( struct kore_json_item *parent, int type, ... ); + +#define JSON_CONSTRUCT_DEBUG + +#ifdef JSON_CONSTRUCT_DEBUG +#define CONSTRUCT_DEBUG(...) kore_log( LOG_DEBUG, __VA_ARGS__ ) +#else +#define CONSTRUCT_DEBUG(...) +#endif + +static struct kore_json_item *json_construct_onev( struct kore_json_item *parent, bool skipname, int type, va_list ap ) +{ + struct kore_json_item *ret = NULL; + const char *name, *parent_name; + + if( type == JSON_END ) + { + fatal( "%s: this shouldn't happen", __func__ ); + return NULL; + } + + if( parent ) + { + if( parent->name ) + parent_name = parent->name; + else parent_name = "(noname)"; + } + else parent_name = "(noparent)"; + + if( !skipname ) + name = va_arg( ap, const char * ); + else name = NULL; + + if( type == JSON_TYPE_OBJECT || type == JSON_TYPE_ARRAY ) + { + const char *strtype = type == JSON_TYPE_OBJECT ? "object" : "array"; + struct kore_json_item *f; + + f = kore_json_create_item( parent, name, type ); + if( !ret ) ret = f; + + CONSTRUCT_DEBUG( "(%s) creating %s \"%s\"", parent_name, strtype, name ?: "" ); + + for( ;; ) + { + int nexttype = va_arg( ap, int ); + if( nexttype == JSON_END ) + { + CONSTRUCT_DEBUG( "(%s) closing %s \"%s\"", parent_name, strtype, name ?: "" ); + return ret; + } + + json_construct_onev( f, type == JSON_TYPE_ARRAY, nexttype, ap ); + } + } + + switch( type ) + { + case JSON_TYPE_STRING: + { + const char *szValue = va_arg( ap, const char * ); + + CONSTRUCT_DEBUG( "(%s) creating string object \"%s\": \"%s\"", parent_name, name ?: "", szValue ); + + ret = kore_json_create_string( parent, name, szValue ); + break; + } + case JSON_TYPE_NUMBER: + { + double flValue = va_arg( ap, double ); + + CONSTRUCT_DEBUG( "(%s) creating number parameter \"%s\": %f", parent_name, name ?: "", flValue ); + + ret = kore_json_create_number( parent, name, flValue ); + break; + } + case JSON_TYPE_LITERAL: + { + int32_t iValue = va_arg( ap, int ); + const char *szValue; + + switch( iValue ) + { + case JSON_FALSE: szValue = "false"; break; + case JSON_TRUE: szValue = "true"; break; + case JSON_NULL: szValue = "null"; break; + default: fatal("illegal value %d for literal type", iValue ); + } + + CONSTRUCT_DEBUG( "(%s) creating literal parameter \"%s\": %s", parent_name, name ?: "", szValue ); + + ret = kore_json_create_literal( parent, name, iValue ); + break; + } + case JSON_TYPE_I32: + { + int32_t iValue = va_arg( ap, int32_t ); + + CONSTRUCT_DEBUG( "(%s) creating int32 parameter \"%s\": %d", parent_name, name ?: "", iValue ); + + ret = kore_json_create_integer( parent, name, iValue ); + break; + } + case JSON_TYPE_U32: + { + uint32_t uValue = va_arg( ap, uint32_t ); + + CONSTRUCT_DEBUG( "(%s) creating uint32 parameter \"%s\": %u", parent_name, name ?: "", uValue ); + + ret = kore_json_create_integer( parent, name, uValue ); + break; + } + case JSON_TYPE_I64: + { + int64_t lValue = va_arg( ap, int64_t ); + + CONSTRUCT_DEBUG( "(%s) creating int64 parameter \"%s\": " PRId64, parent_name, name ?: "", lValue ); + + ret = kore_json_create_integer( parent, name, lValue ); + break; + } + case JSON_TYPE_U64: + { + uint64_t ulValue = va_arg( ap, uint64_t ); + + CONSTRUCT_DEBUG( "(%s) creating uint64 parameter \"%s\": " PRIu64, parent_name, name ?: "", ulValue ); + + ret = kore_json_create_integer_u64( parent, name, ulValue ); + break; + } + } + + return ret; +} + +struct kore_json_item *json_construct( struct kore_json_item *parent, int type, ... ) +{ + va_list ap; + + va_start( ap, type ); + parent = json_construct_onev( parent, type == JSON_TYPE_ARRAY, type, ap ); + va_end( ap ); + + return parent; +}