Coverage Report

Created: 2026-01-09 07:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fwupd/libfwupd/fwupd-json-node.c
Line
Count
Source
1
/*
2
 * Copyright 2025 Richard Hughes <richard@hughsie.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
#include "config.h"
8
9
#include "fwupd-error.h"
10
#include "fwupd-json-array-private.h"
11
#include "fwupd-json-node-private.h"
12
#include "fwupd-json-object-private.h"
13
14
/**
15
 * FwupdJsonNode:
16
 *
17
 * A JSON node.
18
 *
19
 * Nodes are lazy-parsed, and can either be an "object", an "array", a "string" or "raw" -- the
20
 * latter which can be parsed as a float, integer, or boolean.
21
 *
22
 * See also: [struct@FwupdJsonArray] [struct@FwupdJsonObject]
23
 */
24
25
struct FwupdJsonNode {
26
  grefcount refcount;
27
  FwupdJsonNodeKind kind;
28
  gpointer data;
29
  GDestroyNotify destroy_func;
30
};
31
32
/**
33
 * fwupd_json_node_get_kind:
34
 * @self: a #FwupdJsonNode
35
 *
36
 * Gets the kind of the JSON json_node.
37
 *
38
 * Returns: a #FwupdJsonNodeKind
39
 *
40
 * Since: 2.1.1
41
 **/
42
FwupdJsonNodeKind
43
fwupd_json_node_get_kind(FwupdJsonNode *self)
44
0
{
45
0
  g_return_val_if_fail(self != NULL, 0);
46
0
  return self->kind;
47
0
}
48
49
static FwupdJsonNode *
50
fwupd_json_node_new_internal(void)
51
30.2k
{
52
30.2k
  FwupdJsonNode *self = g_new0(FwupdJsonNode, 1);
53
30.2k
  g_ref_count_init(&self->refcount);
54
30.2k
  return self;
55
30.2k
}
56
57
/**
58
 * fwupd_json_node_new_raw: (skip):
59
 * @value: (not nullable): string value
60
 *
61
 * Creates a new JSON string json_node.
62
 *
63
 * Returns: (transfer full): a #FwupdJsonNode
64
 *
65
 * Since: 2.1.1
66
 **/
67
FwupdJsonNode *
68
fwupd_json_node_new_raw(const gchar *value)
69
0
{
70
0
  g_autoptr(FwupdJsonNode) self = fwupd_json_node_new_internal();
71
72
0
  g_return_val_if_fail(value != NULL, NULL);
73
74
0
  self->kind = FWUPD_JSON_NODE_KIND_RAW;
75
0
  self->data = g_ref_string_new(value);
76
0
  self->destroy_func = (GDestroyNotify)g_ref_string_release;
77
0
  return g_steal_pointer(&self);
78
0
}
79
80
FwupdJsonNode *
81
fwupd_json_node_new_raw_internal(GRefString *value)
82
7.38k
{
83
7.38k
  FwupdJsonNode *self = fwupd_json_node_new_internal();
84
7.38k
  self->kind = FWUPD_JSON_NODE_KIND_RAW;
85
7.38k
  self->data = g_ref_string_acquire(value);
86
7.38k
  self->destroy_func = (GDestroyNotify)g_ref_string_release;
87
7.38k
  return self;
88
7.38k
}
89
90
/**
91
 * fwupd_json_node_new_string: (skip):
92
 * @value: (not nullable): string value
93
 *
94
 * Creates a new JSON string json_node.
95
 *
96
 * Returns: (transfer full): a #FwupdJsonNode
97
 *
98
 * Since: 2.1.1
99
 **/
100
FwupdJsonNode *
101
fwupd_json_node_new_string(const gchar *value)
102
0
{
103
0
  g_autoptr(FwupdJsonNode) self = fwupd_json_node_new_internal();
104
0
  self->kind = FWUPD_JSON_NODE_KIND_STRING;
105
0
  if (value != NULL) {
106
0
    self->data = g_ref_string_new(value);
107
0
    self->destroy_func = (GDestroyNotify)g_ref_string_release;
108
0
  }
109
0
  return g_steal_pointer(&self);
110
0
}
111
112
FwupdJsonNode *
113
fwupd_json_node_new_string_internal(GRefString *value)
114
6.63k
{
115
6.63k
  FwupdJsonNode *self = fwupd_json_node_new_internal();
116
6.63k
  self->kind = FWUPD_JSON_NODE_KIND_STRING;
117
6.63k
  if (value != NULL) {
118
5.71k
    self->data = g_ref_string_acquire(value);
119
5.71k
    self->destroy_func = (GDestroyNotify)g_ref_string_release;
120
5.71k
  }
121
6.63k
  return self;
122
6.63k
}
123
124
/**
125
 * fwupd_json_node_new_object: (skip):
126
 * @json_obj: a #FwupdJsonObject
127
 *
128
 * Creates a new JSON object json_node.
129
 *
130
 * Returns: (transfer full): a #FwupdJsonNode
131
 *
132
 * Since: 2.1.1
133
 **/
134
FwupdJsonNode *
135
fwupd_json_node_new_object(FwupdJsonObject *json_obj)
136
8.84k
{
137
8.84k
  g_autoptr(FwupdJsonNode) self = fwupd_json_node_new_internal();
138
139
8.84k
  g_return_val_if_fail(json_obj != NULL, NULL);
140
141
8.84k
  self->kind = FWUPD_JSON_NODE_KIND_OBJECT;
142
8.84k
  self->data = fwupd_json_object_ref(json_obj);
143
8.84k
  self->destroy_func = (GDestroyNotify)fwupd_json_object_unref;
144
8.84k
  return g_steal_pointer(&self);
145
8.84k
}
146
147
/**
148
 * fwupd_json_node_new_array: (skip):
149
 * @json_arr: a #FwupdJsonArray
150
 *
151
 * Creates a new JSON object json_node.
152
 *
153
 * Returns: (transfer full): a #FwupdJsonNode
154
 *
155
 * Since: 2.1.1
156
 **/
157
FwupdJsonNode *
158
fwupd_json_node_new_array(FwupdJsonArray *json_arr)
159
7.37k
{
160
7.37k
  g_autoptr(FwupdJsonNode) self = fwupd_json_node_new_internal();
161
162
7.37k
  g_return_val_if_fail(json_arr != NULL, NULL);
163
164
7.37k
  self->kind = FWUPD_JSON_NODE_KIND_ARRAY;
165
7.37k
  self->data = fwupd_json_array_ref(json_arr);
166
7.37k
  self->destroy_func = (GDestroyNotify)fwupd_json_array_unref;
167
7.37k
  return g_steal_pointer(&self);
168
7.37k
}
169
170
/**
171
 * fwupd_json_node_ref: (skip):
172
 * @self: a #FwupdJsonNode
173
 *
174
 * Increases the reference count of a JSON json_node.
175
 *
176
 * Returns: (transfer full): a #FwupdJsonNode
177
 *
178
 * Since: 2.1.1
179
 **/
180
FwupdJsonNode *
181
fwupd_json_node_ref(FwupdJsonNode *self)
182
0
{
183
0
  g_return_val_if_fail(self != NULL, NULL);
184
0
  g_ref_count_inc(&self->refcount);
185
0
  return self;
186
0
}
187
188
/**
189
 * fwupd_json_node_unref:
190
 * @self: a #FwupdJsonNode
191
 *
192
 * Destroys a JSON json_node.
193
 *
194
 * Returns: (transfer none): a #FwupdJsonArray, or %NULL
195
 *
196
 * Since: 2.1.1
197
 **/
198
FwupdJsonNode *
199
fwupd_json_node_unref(FwupdJsonNode *self)
200
30.2k
{
201
30.2k
  g_return_val_if_fail(self != NULL, NULL);
202
203
30.2k
  if (!g_ref_count_dec(&self->refcount))
204
0
    return self;
205
30.2k
  if (self->destroy_func != NULL)
206
29.3k
    self->destroy_func(self->data);
207
30.2k
  g_free(self);
208
30.2k
  return NULL;
209
30.2k
}
210
211
/**
212
 * fwupd_json_node_get_object: (skip):
213
 * @self: a #FwupdJsonNode
214
 * @error: (nullable): optional return location for an error
215
 *
216
 * Gets the JSON object from a JSON node.
217
 *
218
 * Returns: (transfer none): a #FwupdJsonObject, or %NULL if the node was the wrong kind.
219
 *
220
 * Since: 2.1.1
221
 **/
222
FwupdJsonObject *
223
fwupd_json_node_get_object(FwupdJsonNode *self, GError **error)
224
4.12k
{
225
4.12k
  g_return_val_if_fail(self != NULL, NULL);
226
4.12k
  if (self->kind != FWUPD_JSON_NODE_KIND_OBJECT) {
227
0
    g_set_error(error,
228
0
          FWUPD_ERROR,
229
0
          FWUPD_ERROR_INVALID_DATA,
230
0
          "json_node kind was %s, not object",
231
0
          fwupd_json_node_kind_to_string(self->kind));
232
0
    return NULL;
233
0
  }
234
4.12k
  return fwupd_json_object_ref((FwupdJsonObject *)self->data);
235
4.12k
}
236
237
/**
238
 * fwupd_json_node_get_array: (skip):
239
 * @self: a #FwupdJsonNode
240
 * @error: (nullable): optional return location for an error
241
 *
242
 * Gets the JSON array from a JSON node.
243
 *
244
 * Returns: (transfer none): a #FwupdJsonArray, or %NULL if the node was the wrong kind.
245
 *
246
 * Since: 2.1.1
247
 **/
248
FwupdJsonArray *
249
fwupd_json_node_get_array(FwupdJsonNode *self, GError **error)
250
4.31k
{
251
4.31k
  g_return_val_if_fail(self != NULL, NULL);
252
4.31k
  if (self->kind != FWUPD_JSON_NODE_KIND_ARRAY) {
253
0
    g_set_error(error,
254
0
          FWUPD_ERROR,
255
0
          FWUPD_ERROR_INVALID_DATA,
256
0
          "json_node kind was %s, not array",
257
0
          fwupd_json_node_kind_to_string(self->kind));
258
0
    return NULL;
259
0
  }
260
4.31k
  return fwupd_json_array_ref((FwupdJsonArray *)self->data);
261
4.31k
}
262
263
/**
264
 * fwupd_json_node_get_raw: (skip):
265
 * @self: a #FwupdJsonNode
266
 * @error: (nullable): optional return location for an error
267
 *
268
 * Gets the raw value string from a JSON node.
269
 *
270
 * Returns: a #GRefString, or %NULL if the node was the wrong kind.
271
 *
272
 * Since: 2.1.1
273
 **/
274
GRefString *
275
fwupd_json_node_get_raw(FwupdJsonNode *self, GError **error)
276
3.87k
{
277
3.87k
  g_return_val_if_fail(self != NULL, NULL);
278
3.87k
  if (self->kind != FWUPD_JSON_NODE_KIND_RAW) {
279
0
    g_set_error(error,
280
0
          FWUPD_ERROR,
281
0
          FWUPD_ERROR_INVALID_DATA,
282
0
          "json_node kind was %s, not raw",
283
0
          fwupd_json_node_kind_to_string(self->kind));
284
0
    return NULL;
285
0
  }
286
3.87k
  return (GRefString *)self->data;
287
3.87k
}
288
289
/**
290
 * fwupd_json_node_get_string: (skip):
291
 * @self: a #FwupdJsonNode
292
 * @error: (nullable): optional return location for an error
293
 *
294
 * Gets the JSON string from a JSON node.
295
 *
296
 * Returns: a #GRefString, or %NULL if the node was the wrong kind.
297
 *
298
 * Since: 2.1.1
299
 **/
300
GRefString *
301
fwupd_json_node_get_string(FwupdJsonNode *self, GError **error)
302
0
{
303
0
  g_return_val_if_fail(self != NULL, NULL);
304
0
  if (self->kind != FWUPD_JSON_NODE_KIND_STRING) {
305
0
    g_set_error(error,
306
0
          FWUPD_ERROR,
307
0
          FWUPD_ERROR_INVALID_DATA,
308
0
          "json_node kind was %s, not string",
309
0
          fwupd_json_node_kind_to_string(self->kind));
310
0
    return NULL;
311
0
  }
312
0
  if (self->data == NULL) {
313
0
    g_set_error_literal(error,
314
0
            FWUPD_ERROR,
315
0
            FWUPD_ERROR_NOTHING_TO_DO,
316
0
            "value was null");
317
0
    return NULL;
318
0
  }
319
0
  return (GRefString *)self->data;
320
0
}
321
322
static void
323
fwupd_json_node_append_string_safe(FwupdJsonNode *self, GString *str)
324
3.26k
{
325
3.26k
  const gchar *tmp = (const gchar *)self->data;
326
327
  /* no quotes */
328
3.26k
  if (tmp == NULL) {
329
538
    g_string_append(str, "null");
330
538
    return;
331
538
  }
332
333
  /* quoted and escaped */
334
2.72k
  g_string_append_c(str, '\"');
335
17.6M
  for (guint i = 0; tmp[i] != '\0'; i++) {
336
17.6M
    if (tmp[i] == '\\') {
337
993k
      g_string_append(str, "\\\\");
338
16.6M
    } else if (tmp[i] == '\n') {
339
721
      g_string_append(str, "\\n");
340
16.6M
    } else if (tmp[i] == '\t') {
341
197
      g_string_append(str, "\\t");
342
16.6M
    } else if (tmp[i] == '\"') {
343
280
      g_string_append(str, "\\\"");
344
16.6M
    } else {
345
16.6M
      g_string_append_c(str, tmp[i]);
346
16.6M
    }
347
17.6M
  }
348
2.72k
  g_string_append_c(str, '\"');
349
2.72k
}
350
351
void
352
fwupd_json_node_append_string(FwupdJsonNode *self,
353
            GString *str,
354
            guint depth,
355
            FwupdJsonExportFlags flags)
356
15.5k
{
357
15.5k
  g_return_if_fail(self != NULL);
358
15.5k
  g_return_if_fail(str != NULL);
359
360
15.5k
  if (self->kind == FWUPD_JSON_NODE_KIND_RAW) {
361
3.87k
    g_string_append(str, fwupd_json_node_get_raw(self, NULL));
362
3.87k
    return;
363
3.87k
  }
364
11.7k
  if (self->kind == FWUPD_JSON_NODE_KIND_STRING) {
365
3.26k
    fwupd_json_node_append_string_safe(self, str);
366
3.26k
    return;
367
3.26k
  }
368
8.44k
  if (self->kind == FWUPD_JSON_NODE_KIND_OBJECT) {
369
4.12k
    g_autoptr(FwupdJsonObject) json_obj = fwupd_json_node_get_object(self, NULL);
370
4.12k
    fwupd_json_object_append_string(json_obj, str, depth, flags);
371
4.12k
    return;
372
4.12k
  }
373
4.31k
  if (self->kind == FWUPD_JSON_NODE_KIND_ARRAY) {
374
4.31k
    g_autoptr(FwupdJsonArray) json_arr = fwupd_json_node_get_array(self, NULL);
375
4.31k
    fwupd_json_array_append_string(json_arr, str, depth, flags);
376
4.31k
    return;
377
4.31k
  }
378
4.31k
}
379
380
/**
381
 * fwupd_json_node_to_string:
382
 * @self: a #FwupdJsonNode
383
 * @flags: some #FwupdJsonExportFlags e.g. #FWUPD_JSON_EXPORT_FLAG_INDENT
384
 *
385
 * Converts the JSON json_node to a string representation.
386
 *
387
 * Returns: (transfer full): a #GString
388
 *
389
 * Since: 2.1.1
390
 **/
391
GString *
392
fwupd_json_node_to_string(FwupdJsonNode *self, FwupdJsonExportFlags flags)
393
857
{
394
857
  g_autoptr(GString) str = g_string_new(NULL);
395
857
  g_return_val_if_fail(self != NULL, NULL);
396
857
  fwupd_json_node_append_string(self, str, 0, flags);
397
857
  return g_steal_pointer(&str);
398
857
}