gist/kore_json_construct.c

239 lines
6.3 KiB
C
Raw Normal View History

2022-06-06 03:26:08 +02:00
#include <PublicDomain.txt>
// 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 <stdarg.h>
#include <inttypes.h>
#include <stdbool.h>
#include <kore/kore.h>
#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;
}