summaryrefslogtreecommitdiffstats
path: root/system_cmds/lsmp.tproj/json.h
blob: 2a87a8b2c836526ecde728fa94b2b19412034fbd (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
/*
 * Copyright (c) 2017 Apple Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * The contents of this file constitute Original Code as defined in and
 * are subject to the Apple Public Source License Version 1.1 (the
 * "License").  You may not use this file except in compliance with the
 * License.  Please obtain a copy of the License at
 * http://www.apple.com/publicsource and read it before using this file.
 *
 * This Original Code and all software distributed under the License are
 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License.
 *
 * @APPLE_LICENSE_HEADER_END@
 */

/*
 * Provides a stream-based API for generating JSON output
 *
 * Handles tedious tasks like worrying about comma placement (and avoiding trailing commas).
 * Assumes strings are already escaped (if necessary) and does no error checking (thus it
 * may produce invalid JSON when used improperly).
 *
 * As a convenience, when the provided `json` stream is NULL (i.e. it was never initialized
 * by `JSON_OPEN`) these APIs will do nothing.
 *
 * Example usage:
 *
 *  JSON_t json = JSON_OPEN("/path/to/output.json")
 *  JSON_OBJECT_BEGIN(json); // root object
 *
 *  JSON_OBJECT_SET(json, version, %.1f, 1.0);
 *  JSON_OBJECT_SET_BOOL(json, has_fruit, 1);
 *
 *  // Note the required quotes for strings (formatted or not)
 *  char *mystr = "hello";
 *  JSON_OBJECT_SET(json, formatted_string, "%s", mystr);
 *  JSON_OBJECT_SET(json, literal_string, "my literal string");
 *
 *  JSON_KEY(json, fruit_array);
 *  JSON_ARRAY_BEGIN(json); // fruit_array
 *  JSON_ARRAY_APPEND(json, "my literal string");
 *  JSON_ARRAY_APPEND(json, "<0x%08llx>", 0xface);
 *  JSON_ARRAY_APPEND(json, %d, 3);
 *  JSON_ARRAY_END(json); // fruit_array
 *
 *  JSON_OBJECT_END(json); // root object
 *  JSON_CLOSE(json);
 */

#ifndef _JSON_H_
#define _JSON_H_

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

#define _JSON_IF(json, code) \
  if (json != NULL) { \
    code; \
  }
#define _JSON_COMMA(json) \
  if (json->require_comma) { \
    fprintf(json->stream, ","); \
  }

struct _JSON {
  FILE* stream;
  bool require_comma;
};
typedef struct _JSON * JSON_t;

#pragma mark Open/Close
/* Return a new JSON_t stream */
static inline JSON_t JSON_OPEN(const char *path) {
  JSON_t p = malloc(sizeof(struct _JSON));
  p->stream = fopen(path, "w+");
  p->require_comma = false;
  return p;
}

/* Close an existing JSON stream, removing trailing commas */
#define JSON_CLOSE(json) _JSON_IF(json, fclose(json->stream); free(json))

#pragma mark Keys/Values
/* Output the `key` half of a key/value pair */
#define JSON_KEY(json, key) _JSON_IF(json, _JSON_COMMA(json); fprintf(json->stream, "\"" #key "\":"); json->require_comma = false)
/* Output the `value` half of a key/value pair */
#define JSON_VALUE(json, format, ...) _JSON_IF(json, fprintf(json->stream, #format, ##__VA_ARGS__); json->require_comma = true)

#define _JSON_BEGIN(json, character) _JSON_COMMA(json); fprintf(json->stream, #character); json->require_comma = false;
#define _JSON_END(json, character) fprintf(json->stream, #character); json->require_comma = true;
#define _JSON_BOOL(val) ( val ? "true" : "false" )

#pragma mark Objects
/* Start a new JSON object */
#define JSON_OBJECT_BEGIN(json) _JSON_IF(json, _JSON_BEGIN(json, {))
/* Set a value in the current JSON object */
#define JSON_OBJECT_SET(json, key, format, ...) _JSON_IF(json, JSON_KEY(json, key); JSON_VALUE(json, format, ##__VA_ARGS__))
/* Set a boolean in the current JSON object */
#define JSON_OBJECT_SET_BOOL(json, key, value) JSON_OBJECT_SET(json, key, %s, _JSON_BOOL(value))
/* End the current JSON object */
#define JSON_OBJECT_END(json) _JSON_IF(json, _JSON_END(json, }))

#pragma mark Arrays
/* Start a new JSON array */
#define JSON_ARRAY_BEGIN(json) _JSON_IF(json, _JSON_BEGIN(json, [))
/* Append a value to the current JSON array */
#define JSON_ARRAY_APPEND(json, format, ...) _JSON_IF(json, _JSON_COMMA(json); JSON_VALUE(json, format, ##__VA_ARGS__))
/* End the current JSON array */
#define JSON_ARRAY_END(json) _JSON_IF(json, _JSON_END(json, ]))

#endif /* _JSON_H_ */