mongory 0.6.2 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +2 -0
  3. data/CHANGELOG.md +42 -0
  4. data/README.md +82 -176
  5. data/Rakefile +77 -0
  6. data/SUBMODULE_INTEGRATION.md +325 -0
  7. data/docs/advanced_usage.md +40 -0
  8. data/docs/clang_bridge.md +69 -0
  9. data/docs/field_names.md +30 -0
  10. data/docs/migration.md +30 -0
  11. data/docs/performance.md +61 -0
  12. data/examples/benchmark.rb +98 -19
  13. data/ext/mongory_ext/extconf.rb +91 -0
  14. data/ext/mongory_ext/mongory-core/LICENSE +3 -0
  15. data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/array.h +105 -0
  16. data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/config.h +206 -0
  17. data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/error.h +82 -0
  18. data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/memory_pool.h +95 -0
  19. data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/table.h +108 -0
  20. data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/value.h +175 -0
  21. data/ext/mongory_ext/mongory-core/include/mongory-core/matchers/matcher.h +76 -0
  22. data/ext/mongory_ext/mongory-core/include/mongory-core.h +12 -0
  23. data/ext/mongory_ext/mongory-core/src/foundations/array.c +246 -0
  24. data/ext/mongory_ext/mongory-core/src/foundations/array_private.h +18 -0
  25. data/ext/mongory_ext/mongory-core/src/foundations/config.c +406 -0
  26. data/ext/mongory_ext/mongory-core/src/foundations/config_private.h +30 -0
  27. data/ext/mongory_ext/mongory-core/src/foundations/error.c +30 -0
  28. data/ext/mongory_ext/mongory-core/src/foundations/memory_pool.c +298 -0
  29. data/ext/mongory_ext/mongory-core/src/foundations/string_buffer.c +65 -0
  30. data/ext/mongory_ext/mongory-core/src/foundations/string_buffer.h +49 -0
  31. data/ext/mongory_ext/mongory-core/src/foundations/table.c +458 -0
  32. data/ext/mongory_ext/mongory-core/src/foundations/value.c +459 -0
  33. data/ext/mongory_ext/mongory-core/src/matchers/array_record_matcher.c +309 -0
  34. data/ext/mongory_ext/mongory-core/src/matchers/array_record_matcher.h +47 -0
  35. data/ext/mongory_ext/mongory-core/src/matchers/base_matcher.c +161 -0
  36. data/ext/mongory_ext/mongory-core/src/matchers/base_matcher.h +115 -0
  37. data/ext/mongory_ext/mongory-core/src/matchers/compare_matcher.c +210 -0
  38. data/ext/mongory_ext/mongory-core/src/matchers/compare_matcher.h +83 -0
  39. data/ext/mongory_ext/mongory-core/src/matchers/composite_matcher.c +539 -0
  40. data/ext/mongory_ext/mongory-core/src/matchers/composite_matcher.h +125 -0
  41. data/ext/mongory_ext/mongory-core/src/matchers/existance_matcher.c +144 -0
  42. data/ext/mongory_ext/mongory-core/src/matchers/existance_matcher.h +48 -0
  43. data/ext/mongory_ext/mongory-core/src/matchers/external_matcher.c +121 -0
  44. data/ext/mongory_ext/mongory-core/src/matchers/external_matcher.h +46 -0
  45. data/ext/mongory_ext/mongory-core/src/matchers/inclusion_matcher.c +199 -0
  46. data/ext/mongory_ext/mongory-core/src/matchers/inclusion_matcher.h +46 -0
  47. data/ext/mongory_ext/mongory-core/src/matchers/literal_matcher.c +334 -0
  48. data/ext/mongory_ext/mongory-core/src/matchers/literal_matcher.h +97 -0
  49. data/ext/mongory_ext/mongory-core/src/matchers/matcher.c +196 -0
  50. data/ext/mongory_ext/mongory-core/src/matchers/matcher_explainable.c +107 -0
  51. data/ext/mongory_ext/mongory-core/src/matchers/matcher_explainable.h +50 -0
  52. data/ext/mongory_ext/mongory-core/src/matchers/matcher_traversable.c +55 -0
  53. data/ext/mongory_ext/mongory-core/src/matchers/matcher_traversable.h +23 -0
  54. data/ext/mongory_ext/mongory_ext.c +635 -0
  55. data/lib/mongory/c_query_builder.rb +44 -0
  56. data/lib/mongory/converters/converted.rb +2 -2
  57. data/lib/mongory/matchers/abstract_matcher.rb +4 -0
  58. data/lib/mongory/matchers/abstract_multi_matcher.rb +44 -0
  59. data/lib/mongory/matchers/and_matcher.rb +2 -23
  60. data/lib/mongory/matchers/array_record_matcher.rb +2 -23
  61. data/lib/mongory/matchers/elem_match_matcher.rb +4 -0
  62. data/lib/mongory/matchers/eq_matcher.rb +4 -0
  63. data/lib/mongory/matchers/every_matcher.rb +4 -0
  64. data/lib/mongory/matchers/exists_matcher.rb +4 -0
  65. data/lib/mongory/matchers/field_matcher.rb +4 -0
  66. data/lib/mongory/matchers/gt_matcher.rb +4 -0
  67. data/lib/mongory/matchers/gte_matcher.rb +4 -0
  68. data/lib/mongory/matchers/hash_condition_matcher.rb +2 -23
  69. data/lib/mongory/matchers/in_matcher.rb +13 -4
  70. data/lib/mongory/matchers/literal_matcher.rb +4 -0
  71. data/lib/mongory/matchers/lt_matcher.rb +4 -0
  72. data/lib/mongory/matchers/lte_matcher.rb +4 -0
  73. data/lib/mongory/matchers/ne_matcher.rb +4 -0
  74. data/lib/mongory/matchers/nin_matcher.rb +14 -5
  75. data/lib/mongory/matchers/not_matcher.rb +4 -0
  76. data/lib/mongory/matchers/or_matcher.rb +2 -23
  77. data/lib/mongory/matchers/present_matcher.rb +4 -0
  78. data/lib/mongory/matchers/regex_matcher.rb +4 -0
  79. data/lib/mongory/matchers/size_matcher.rb +4 -0
  80. data/lib/mongory/query_builder.rb +8 -0
  81. data/lib/mongory/utils/context.rb +7 -0
  82. data/lib/mongory/utils.rb +1 -1
  83. data/lib/mongory/version.rb +1 -1
  84. data/lib/mongory.rb +7 -0
  85. data/mongory.gemspec +10 -4
  86. data/scripts/build_with_core.sh +292 -0
  87. metadata +70 -5
@@ -0,0 +1,459 @@
1
+ /**
2
+ * @file value.c
3
+ * @brief Implements the mongory_value generic value type and its operations.
4
+ *
5
+ * This file provides functions for creating (`wrap`), comparing, and
6
+ * converting `mongory_value` instances to strings. Each data type supported by
7
+ * `mongory_value` has a corresponding comparison function and wrapping
8
+ * function.
9
+ */
10
+ #include "string_buffer.h"
11
+ #include <mongory-core/foundations/array.h>
12
+ #include <mongory-core/foundations/config.h> // For mongory_string_cpy
13
+ #include <mongory-core/foundations/memory_pool.h>
14
+ #include <mongory-core/foundations/table.h>
15
+ #include <mongory-core/foundations/value.h>
16
+ #include "config_private.h"
17
+ #include <stddef.h> // For NULL
18
+ #include <stdio.h> // For snprintf
19
+ #include <stdlib.h> // For general utilities (not directly used here but common)
20
+ #include <string.h> // For strcmp
21
+
22
+ /**
23
+ * @brief Returns a string literal representing the type of the mongory_value.
24
+ * @param value The mongory_value to inspect.
25
+ * @return A string like "Int", "String", "Array", etc., or "UnknownType".
26
+ */
27
+ char *mongory_type_to_string(mongory_value *value) {
28
+ if (!value)
29
+ return "NullValuePtr"; // Handle null pointer to value itself
30
+ switch (value->type) {
31
+ // Use X-Macro to generate cases for each defined type.
32
+ #define CASE_GEN(name, num, str, field) \
33
+ case name: \
34
+ return str;
35
+ MONGORY_TYPE_MACRO(CASE_GEN)
36
+ #undef CASE_GEN
37
+ default:
38
+ return "UnknownType"; // Fallback for unrecognized types.
39
+ }
40
+ }
41
+
42
+ /**
43
+ * @brief Extracts a void pointer to the raw data within the mongory_value.
44
+ * The caller must know the actual type to cast this pointer correctly.
45
+ * @param value The mongory_value from which to extract data.
46
+ * @return A void pointer to the data, or NULL for unknown types or MONGORY_TYPE_NULL.
47
+ */
48
+ void *mongory_value_extract(mongory_value *value) {
49
+ if (!value)
50
+ return NULL;
51
+ switch (value->type) {
52
+ // Use X-Macro to generate cases. For MONGORY_TYPE_NULL, &value->data.i is
53
+ // returned but is meaningless as data for NULL.
54
+ #define EXTRACT_CASE(name, num, str, field) \
55
+ case name: \
56
+ return (void *)&value->data.field;
57
+ MONGORY_TYPE_MACRO(EXTRACT_CASE)
58
+ #undef EXTRACT_CASE
59
+ default:
60
+ return NULL; // Unknown type.
61
+ }
62
+ }
63
+
64
+ static char *mongory_value_null_to_str(mongory_value *value, mongory_memory_pool *pool);
65
+ static char *mongory_value_bool_to_str(mongory_value *value, mongory_memory_pool *pool);
66
+ static char *mongory_value_int_to_str(mongory_value *value, mongory_memory_pool *pool);
67
+ static char *mongory_value_double_to_str(mongory_value *value, mongory_memory_pool *pool);
68
+ static char *mongory_value_string_to_str(mongory_value *value, mongory_memory_pool *pool);
69
+ static char *mongory_value_array_to_str(mongory_value *value, mongory_memory_pool *pool);
70
+ static char *mongory_value_table_to_str(mongory_value *value, mongory_memory_pool *pool);
71
+ static char *mongory_value_generic_ptr_to_str(mongory_value *value, mongory_memory_pool *pool);
72
+
73
+ /**
74
+ * @brief Internal helper to allocate a new mongory_value structure from a pool.
75
+ * @param pool The memory pool to allocate from.
76
+ * @return A pointer to the new mongory_value, or NULL on allocation failure.
77
+ */
78
+ static inline mongory_value *mongory_value_new(mongory_memory_pool *pool) {
79
+ if (!pool || !pool->alloc)
80
+ return NULL; // Invalid pool.
81
+ mongory_value *value = MG_ALLOC_PTR(pool, mongory_value);
82
+ if (!value) {
83
+ // TODO: pool->error could be set by pool->alloc itself.
84
+ return NULL;
85
+ }
86
+ value->pool = pool;
87
+ value->origin = NULL; // Default origin to NULL.
88
+ return value;
89
+ }
90
+
91
+ // ============================================================================
92
+ // Comparison Functions
93
+ //
94
+ // Each data type has a corresponding comparison function. These functions
95
+ // are assigned to the `comp` function pointer in the `mongory_value` struct.
96
+ // They typically handle comparisons with other compatible types (e.g., Int
97
+ // can be compared with Double).
98
+ // ============================================================================
99
+
100
+ /** Compares two MONGORY_TYPE_NULL values. Always equal. */
101
+ static inline int mongory_value_null_compare(mongory_value *a, mongory_value *b) {
102
+ (void)a; // 'a' is implicitly MONGORY_TYPE_NULL by context of call.
103
+ if (b->type != MONGORY_TYPE_NULL) {
104
+ return mongory_value_compare_fail; // Cannot compare NULL with non-NULL.
105
+ }
106
+ return 0; // NULL is equal to NULL.
107
+ }
108
+
109
+ /** Wraps a NULL value. The `n` parameter is ignored. */
110
+ mongory_value *mongory_value_wrap_n(mongory_memory_pool *pool, void *n) {
111
+ (void)n; // Parameter 'n' is unused for wrapping NULL.
112
+ mongory_value *value = mongory_value_new(pool);
113
+ if (!value)
114
+ return NULL;
115
+ value->type = MONGORY_TYPE_NULL;
116
+ value->comp = mongory_value_null_compare;
117
+ value->to_str = mongory_value_null_to_str;
118
+ // data union is not explicitly set for NULL.
119
+ return value;
120
+ }
121
+
122
+ // ============================================================================
123
+ // Wrapper Functions
124
+ //
125
+ // These functions create a `mongory_value` and wrap a native C type
126
+ // (like bool, int, char*). They allocate the value from the memory pool and
127
+ // set its type, data, and function pointers (`comp`, `to_str`).
128
+ // ============================================================================
129
+
130
+ /** Compares two MONGORY_TYPE_BOOL values. */
131
+ static inline int mongory_value_bool_compare(mongory_value *a, mongory_value *b) {
132
+ if (b->type != MONGORY_TYPE_BOOL)
133
+ return mongory_value_compare_fail;
134
+ // Standard comparison: (true > false)
135
+ return (a->data.b > b->data.b) - (a->data.b < b->data.b);
136
+ }
137
+
138
+ /** Wraps a boolean value. */
139
+ mongory_value *mongory_value_wrap_b(mongory_memory_pool *pool, bool b_val) {
140
+ mongory_value *value = mongory_value_new(pool);
141
+ if (!value)
142
+ return NULL;
143
+ value->type = MONGORY_TYPE_BOOL;
144
+ value->data.b = b_val;
145
+ value->comp = mongory_value_bool_compare;
146
+ value->to_str = mongory_value_bool_to_str;
147
+ return value;
148
+ }
149
+
150
+ /** Compares MONGORY_TYPE_INT with INT or DOUBLE. */
151
+ static inline int mongory_value_int_compare(mongory_value *a, mongory_value *b) {
152
+ if (b->type == MONGORY_TYPE_DOUBLE) {
153
+ // Compare int (a) as double with double (b).
154
+ double a_as_double = (double)a->data.i;
155
+ double b_value = b->data.d;
156
+ return (a_as_double > b_value) - (a_as_double < b_value);
157
+ }
158
+ if (b->type == MONGORY_TYPE_INT) {
159
+ // Compare int (a) with int (b).
160
+ int64_t a_value = a->data.i;
161
+ int64_t b_value = b->data.i;
162
+ return (a_value > b_value) - (a_value < b_value);
163
+ }
164
+ return mongory_value_compare_fail; // Incompatible type for comparison.
165
+ }
166
+
167
+ /** Wraps an integer (promoted to int64_t). */
168
+ mongory_value *mongory_value_wrap_i(mongory_memory_pool *pool, int i_val) {
169
+ mongory_value *value = mongory_value_new(pool);
170
+ if (!value)
171
+ return NULL;
172
+ value->type = MONGORY_TYPE_INT;
173
+ value->data.i = (int64_t)i_val; // Store as int64_t.
174
+ value->comp = mongory_value_int_compare;
175
+ value->to_str = mongory_value_int_to_str;
176
+ return value;
177
+ }
178
+
179
+ /** Compares MONGORY_TYPE_DOUBLE with DOUBLE or INT. */
180
+ static inline int mongory_value_double_compare(mongory_value *a, mongory_value *b) {
181
+ if (b->type == MONGORY_TYPE_DOUBLE) {
182
+ double a_value = a->data.d;
183
+ double b_value = b->data.d;
184
+ return (a_value > b_value) - (a_value < b_value);
185
+ }
186
+ if (b->type == MONGORY_TYPE_INT) {
187
+ // Compare double (a) with int (b) as double.
188
+ double a_value = a->data.d;
189
+ double b_as_double = (double)b->data.i;
190
+ return (a_value > b_as_double) - (a_value < b_as_double);
191
+ }
192
+ return mongory_value_compare_fail; // Incompatible type.
193
+ }
194
+
195
+ /** Wraps a double value. */
196
+ mongory_value *mongory_value_wrap_d(mongory_memory_pool *pool, double d_val) {
197
+ mongory_value *value = mongory_value_new(pool);
198
+ if (!value)
199
+ return NULL;
200
+ value->type = MONGORY_TYPE_DOUBLE;
201
+ value->data.d = d_val;
202
+ value->comp = mongory_value_double_compare;
203
+ value->to_str = mongory_value_double_to_str;
204
+ return value;
205
+ }
206
+
207
+ /** Compares two MONGORY_TYPE_STRING values lexicographically. */
208
+ static inline int mongory_value_string_compare(mongory_value *a, mongory_value *b) {
209
+ // Ensure both are strings and not NULL pointers.
210
+ if (b->type != MONGORY_TYPE_STRING || a->data.s == NULL || b->data.s == NULL) {
211
+ // If one is NULL string and other is not, how to compare?
212
+ // For now, strict: both must be valid strings.
213
+ // Or, define NULL string < non-NULL string.
214
+ // Current: fail if not string or if actual char* is NULL.
215
+ return mongory_value_compare_fail;
216
+ }
217
+ int cmp_result = strcmp(a->data.s, b->data.s);
218
+ return (cmp_result > 0) - (cmp_result < 0); // Normalize to -1, 0, 1
219
+ }
220
+
221
+ /** Wraps a string. Makes a copy of the string using the pool. */
222
+ mongory_value *mongory_value_wrap_s(mongory_memory_pool *pool, char *s_val) {
223
+ mongory_value *value = mongory_value_new(pool);
224
+ if (!value)
225
+ return NULL;
226
+ value->type = MONGORY_TYPE_STRING;
227
+ // `mongory_string_cpy` handles the allocation and copying of the string.
228
+ // If `s_val` is NULL, `value->data.s` will be NULL.
229
+ // If allocation fails, `value->data.s` will also be NULL.
230
+ // TODO: This function should ideally return NULL if the string copy fails
231
+ // when `s_val` was not NULL, but this requires a more complex error handling
232
+ // strategy with the memory pool.
233
+ value->data.s = mongory_string_cpy(pool, s_val);
234
+ value->comp = mongory_value_string_compare;
235
+ value->to_str = mongory_value_string_to_str;
236
+ return value;
237
+ }
238
+
239
+ /** Compares two MONGORY_TYPE_ARRAY values element by element. */
240
+ static inline int mongory_value_array_compare(mongory_value *a, mongory_value *b) {
241
+ if (b->type != MONGORY_TYPE_ARRAY || a->data.a == NULL || b->data.a == NULL) {
242
+ return mongory_value_compare_fail; // Must be two valid arrays.
243
+ }
244
+ struct mongory_array *array_a = a->data.a;
245
+ struct mongory_array *array_b = b->data.a;
246
+
247
+ // First, compare by array size. An array with fewer elements is "less than"
248
+ // an array with more elements.
249
+ if (array_a->count != array_b->count) {
250
+ return (array_a->count > array_b->count) - (array_a->count < array_b->count);
251
+ }
252
+
253
+ // Same count, compare element by element.
254
+ for (size_t i = 0; i < array_a->count; i++) {
255
+ mongory_value *item_a = array_a->get(array_a, i);
256
+ mongory_value *item_b = array_b->get(array_b, i);
257
+
258
+ // Handle NULL elements within arrays carefully.
259
+ bool a_item_is_null = (item_a == NULL || item_a->type == MONGORY_TYPE_NULL);
260
+ bool b_item_is_null = (item_b == NULL || item_b->type == MONGORY_TYPE_NULL);
261
+
262
+ if (a_item_is_null && b_item_is_null)
263
+ continue; // Both null, considered equal here.
264
+ if (a_item_is_null)
265
+ return -1; // Null is less than non-null.
266
+ if (b_item_is_null)
267
+ return 1; // Non-null is greater than null.
268
+
269
+ // Both items are non-null, compare them using their own comp functions.
270
+ if (!item_a->comp || !item_b->comp)
271
+ return mongory_value_compare_fail; // Should not happen for valid values
272
+
273
+ int cmp_result = item_a->comp(item_a, item_b);
274
+ if (cmp_result == mongory_value_compare_fail)
275
+ return mongory_value_compare_fail; // Incomparable elements
276
+ if (cmp_result != 0) {
277
+ return cmp_result; // First differing element determines array order.
278
+ }
279
+ }
280
+ return 0; // All elements are equal.
281
+ }
282
+
283
+ /** Wraps a mongory_array. */
284
+ mongory_value *mongory_value_wrap_a(mongory_memory_pool *pool, struct mongory_array *a_val) {
285
+ mongory_value *value = mongory_value_new(pool);
286
+ if (!value)
287
+ return NULL;
288
+ value->type = MONGORY_TYPE_ARRAY;
289
+ value->data.a = a_val;
290
+ value->comp = mongory_value_array_compare;
291
+ value->to_str = mongory_value_array_to_str;
292
+ return value;
293
+ }
294
+
295
+ /** Compares two MONGORY_TYPE_TABLE values. Currently unsupported (always fails). */
296
+ static inline int mongory_value_table_compare(mongory_value *a, mongory_value *b) {
297
+ (void)a; // Unused.
298
+ (void)b; // Unused.
299
+ // True table comparison is complex because the order of keys is not
300
+ // significant. It would require iterating over keys of one table and checking
301
+ // for their presence and value equality in the other. This is not
302
+ // implemented. Therefore, tables are considered incomparable.
303
+ return mongory_value_compare_fail;
304
+ }
305
+
306
+ /** Wraps a mongory_table. */
307
+ mongory_value *mongory_value_wrap_t(mongory_memory_pool *pool, struct mongory_table *t_val) {
308
+ mongory_value *value = mongory_value_new(pool);
309
+ if (!value)
310
+ return NULL;
311
+ value->type = MONGORY_TYPE_TABLE;
312
+ value->data.t = t_val;
313
+ value->comp = mongory_value_table_compare;
314
+ value->to_str = mongory_value_table_to_str;
315
+ return value;
316
+ }
317
+
318
+ /** Generic comparison for types not otherwise handled (e.g. POINTER, REGEX, UNSUPPORTED). Always fails. */
319
+ static inline int mongory_value_generic_ptr_compare(mongory_value *a, mongory_value *b) {
320
+ (void)a; // Unused.
321
+ (void)b; // Unused.
322
+ // Generic pointers are generally not comparable in a meaningful way beyond identity.
323
+ return mongory_value_compare_fail;
324
+ }
325
+
326
+ /** Wraps an unsupported/unknown type pointer. */
327
+ mongory_value *mongory_value_wrap_u(mongory_memory_pool *pool, void *u_val) {
328
+ mongory_value *value = mongory_value_new(pool);
329
+ if (!value)
330
+ return NULL;
331
+ value->type = MONGORY_TYPE_UNSUPPORTED;
332
+ value->data.u = u_val;
333
+ value->comp = mongory_value_generic_ptr_compare; // Unsupported types are not comparable.
334
+ value->to_str = mongory_value_generic_ptr_to_str;
335
+ return value;
336
+ }
337
+
338
+ static char *mongory_value_regex_to_str(mongory_value *value, mongory_memory_pool *pool) {
339
+ char *str = mongory_internal_regex_adapter->stringify_func(pool, value);
340
+ if (!str)
341
+ return NULL;
342
+ return str;
343
+ }
344
+
345
+ /** Wraps a regex type pointer. */
346
+ mongory_value *mongory_value_wrap_regex(mongory_memory_pool *pool, void *regex_val) {
347
+ mongory_value *value = mongory_value_new(pool);
348
+ if (!value)
349
+ return NULL;
350
+ value->type = MONGORY_TYPE_REGEX;
351
+ value->data.regex = regex_val;
352
+ value->comp = mongory_value_generic_ptr_compare; // Regex values are not directly comparable.
353
+ value->to_str = mongory_value_regex_to_str;
354
+ return value;
355
+ }
356
+
357
+ /** Wraps a generic void pointer. */
358
+ mongory_value *mongory_value_wrap_ptr(mongory_memory_pool *pool, void *ptr_val) {
359
+ mongory_value *value = mongory_value_new(pool);
360
+ if (!value)
361
+ return NULL;
362
+ value->type = MONGORY_TYPE_POINTER;
363
+ value->data.ptr = ptr_val;
364
+ value->comp = mongory_value_generic_ptr_compare; // Generic pointers are not comparable.
365
+ value->to_str = mongory_value_generic_ptr_to_str;
366
+ return value;
367
+ }
368
+
369
+ // ============================================================================
370
+ // Stringify Functions
371
+ //
372
+ // Each data type has a corresponding `to_str` function that converts the
373
+ // value to a string representation (often JSON-like) and appends it to a
374
+ // `mongory_string_buffer`.
375
+ // ============================================================================
376
+
377
+ static char *mongory_value_null_to_str(mongory_value *value, mongory_memory_pool *pool) {
378
+ (void)value;
379
+ return mongory_string_cpy(pool, "null");
380
+ }
381
+
382
+ static char *mongory_value_bool_to_str(mongory_value *value, mongory_memory_pool *pool) {
383
+ return mongory_string_cpy(pool, value->data.b ? "true" : "false");
384
+ }
385
+
386
+ static char *mongory_value_int_to_str(mongory_value *value, mongory_memory_pool *pool) {
387
+ return mongory_string_cpyf(pool, "%lld", (long long)value->data.i);
388
+ }
389
+
390
+ static char *mongory_value_double_to_str(mongory_value *value, mongory_memory_pool *pool) {
391
+ return mongory_string_cpyf(pool, "%f", value->data.d);
392
+ }
393
+
394
+ static char *mongory_value_string_to_str(mongory_value *value, mongory_memory_pool *pool) {
395
+ return mongory_string_cpyf(pool, "\"%s\"", value->data.s);
396
+ }
397
+
398
+ typedef struct mongory_value_container_to_str_ctx {
399
+ size_t count;
400
+ size_t total;
401
+ mongory_string_buffer *buffer; /**< The string buffer to append to. */
402
+ } mongory_value_container_to_str_ctx;
403
+
404
+ static bool mongory_value_array_to_str_each(mongory_value *value, void *ctx) {
405
+ mongory_value_container_to_str_ctx *context = (mongory_value_container_to_str_ctx *)ctx;
406
+ mongory_string_buffer *buffer = context->buffer;
407
+ char *str = value->to_str(value, buffer->pool);
408
+ if (!str)
409
+ return false;
410
+ mongory_string_buffer_append(buffer, str);
411
+ context->count++;
412
+ if (context->count < context->total) {
413
+ mongory_string_buffer_append(buffer, ",");
414
+ }
415
+ return true;
416
+ }
417
+
418
+ static char *mongory_value_array_to_str(mongory_value *value, mongory_memory_pool *pool) {
419
+ mongory_string_buffer *buffer = mongory_string_buffer_new(pool);
420
+ if (!buffer)
421
+ return NULL;
422
+ mongory_string_buffer_append(buffer, "[");
423
+ struct mongory_array *array = value->data.a;
424
+ mongory_value_container_to_str_ctx ctx = {.count = 0, .total = array->count, .buffer = buffer};
425
+ array->each(array, &ctx, mongory_value_array_to_str_each);
426
+ mongory_string_buffer_append(buffer, "]");
427
+ return mongory_string_buffer_cstr(buffer);
428
+ }
429
+
430
+ static bool mongory_value_table_to_str_each(char *key, mongory_value *value, void *ctx) {
431
+ mongory_value_container_to_str_ctx *context = (mongory_value_container_to_str_ctx *)ctx;
432
+ mongory_string_buffer *buffer = context->buffer;
433
+ mongory_string_buffer_appendf(buffer, "\"%s\":", key);
434
+ char *str = value->to_str(value, buffer->pool);
435
+ if (!str)
436
+ return false;
437
+ mongory_string_buffer_append(buffer, str);
438
+ context->count++;
439
+ if (context->count < context->total) {
440
+ mongory_string_buffer_append(buffer, ",");
441
+ }
442
+ return true;
443
+ }
444
+
445
+ static char *mongory_value_table_to_str(mongory_value *value, mongory_memory_pool *pool) {
446
+ mongory_string_buffer *buffer = mongory_string_buffer_new(pool);
447
+ if (!buffer)
448
+ return NULL;
449
+ mongory_string_buffer_append(buffer, "{");
450
+ struct mongory_table *table = value->data.t;
451
+ mongory_value_container_to_str_ctx ctx = {.count = 0, .total = table->count, .buffer = buffer};
452
+ table->each(table, &ctx, mongory_value_table_to_str_each);
453
+ mongory_string_buffer_append(buffer, "}");
454
+ return mongory_string_buffer_cstr(buffer);
455
+ }
456
+
457
+ static char *mongory_value_generic_ptr_to_str(mongory_value *value, mongory_memory_pool *pool) {
458
+ return mongory_string_cpyf(pool, "%p", value->data.ptr);
459
+ }