@@ -250,6 +250,198 @@ char *fossil_media_json_roundtrip(const char *json_text, int pretty, fossil_medi
250250 */
251251const char *fossil_media_json_type_name (fossil_media_json_type_t t);
252252
253+ /* * @name Clone & Equality
254+ * @{
255+ */
256+
257+ /* *
258+ * @brief Deep-copy a JSON value.
259+ *
260+ * Recursively clones the entire JSON value and its children.
261+ *
262+ * @param src Source JSON value (must not be NULL).
263+ * @return Newly allocated JSON value on success, or NULL on failure.
264+ */
265+ fossil_media_json_value_t *
266+ fossil_media_json_clone (const fossil_media_json_value_t *src);
267+
268+ /* *
269+ * @brief Compare two JSON values for equality.
270+ *
271+ * Performs a deep structural and value comparison.
272+ *
273+ * @param a First JSON value.
274+ * @param b Second JSON value.
275+ * @return 1 if equal, 0 if not equal, -1 on error.
276+ */
277+ int fossil_media_json_equals (const fossil_media_json_value_t *a,
278+ const fossil_media_json_value_t *b);
279+
280+ /* * @} */
281+
282+ /* * @name Type Helpers
283+ * @{
284+ */
285+
286+ /* *
287+ * @brief Check if a JSON value is null.
288+ *
289+ * @param v JSON value to check.
290+ * @return 1 if null, 0 otherwise.
291+ */
292+ int fossil_media_json_is_null (const fossil_media_json_value_t *v);
293+
294+ /* *
295+ * @brief Check if a JSON value is an array.
296+ *
297+ * @param v JSON value to check.
298+ * @return 1 if array, 0 otherwise.
299+ */
300+ int fossil_media_json_is_array (const fossil_media_json_value_t *v);
301+
302+ /* *
303+ * @brief Check if a JSON value is an object.
304+ *
305+ * @param v JSON value to check.
306+ * @return 1 if object, 0 otherwise.
307+ */
308+ int fossil_media_json_is_object (const fossil_media_json_value_t *v);
309+
310+ /* * @} */
311+
312+ /* * @name Memory & Capacity
313+ * @{
314+ */
315+
316+ /* *
317+ * @brief Reserve capacity for a JSON array.
318+ *
319+ * Ensures that the array can hold at least `capacity` items without resizing.
320+ *
321+ * @param arr JSON array value (must be of type ARRAY).
322+ * @param capacity Desired capacity.
323+ * @return 0 on success, nonzero on error.
324+ */
325+ int fossil_media_json_array_reserve (fossil_media_json_value_t *arr, size_t capacity);
326+
327+ /* *
328+ * @brief Reserve capacity for a JSON object.
329+ *
330+ * Ensures that the object can hold at least `capacity` key/value pairs.
331+ *
332+ * @param obj JSON object value (must be of type OBJECT).
333+ * @param capacity Desired capacity.
334+ * @return 0 on success, nonzero on error.
335+ */
336+ int fossil_media_json_object_reserve (fossil_media_json_value_t *obj, size_t capacity);
337+
338+ /* * @} */
339+
340+ /* * @name File I/O
341+ * @{
342+ */
343+
344+ /* *
345+ * @brief Parse a JSON file into a DOM tree.
346+ *
347+ * Reads the entire file and parses it into an internal DOM structure.
348+ *
349+ * @param filename Path to JSON file.
350+ * @param err_out Optional pointer to error details.
351+ * @return Pointer to the parsed JSON value, or NULL on failure.
352+ */
353+ fossil_media_json_value_t *
354+ fossil_media_json_parse_file (const char *filename, fossil_media_json_error_t *err_out);
355+
356+ /* *
357+ * @brief Write a JSON value to a file.
358+ *
359+ * Serializes the JSON value and writes it to the given file.
360+ *
361+ * @param v JSON value to write.
362+ * @param filename Path to output file.
363+ * @param pretty Nonzero for human-readable indentation.
364+ * @param err_out Optional pointer to error details.
365+ * @return 0 on success, nonzero on error.
366+ */
367+ int fossil_media_json_write_file (const fossil_media_json_value_t *v,
368+ const char *filename,
369+ int pretty,
370+ fossil_media_json_error_t *err_out);
371+
372+ /* * @} */
373+
374+ /* * @name Number Handling
375+ * @{
376+ */
377+
378+ /* *
379+ * @brief Create a JSON integer value.
380+ *
381+ * Stores an integer in a JSON number node (lossless if within range).
382+ *
383+ * @param i Integer value.
384+ * @return Newly allocated JSON number value, or NULL if allocation fails.
385+ */
386+ fossil_media_json_value_t *fossil_media_json_new_int (long long i);
387+
388+ /* *
389+ * @brief Get an integer from a JSON number.
390+ *
391+ * @param v JSON number value.
392+ * @param out Output pointer to receive integer value.
393+ * @return 0 on success, nonzero if not a number or out of range.
394+ */
395+ int fossil_media_json_get_int (const fossil_media_json_value_t *v, long long *out);
396+
397+ /* * @} */
398+
399+ /* * @name Debug & Validation
400+ * @{
401+ */
402+
403+ /* *
404+ * @brief Print a debug dump of a JSON value.
405+ *
406+ * Dumps the JSON tree in a human-readable indented format for debugging.
407+ *
408+ * @param v JSON value to dump.
409+ * @param indent Starting indentation level.
410+ */
411+ void fossil_media_json_debug_dump (const fossil_media_json_value_t *v, int indent);
412+
413+ /* *
414+ * @brief Validate JSON text without building a DOM.
415+ *
416+ * Parses the text and discards the result, returning only validity status.
417+ *
418+ * @param json_text Input JSON text (NUL-terminated).
419+ * @param err_out Optional pointer to error details.
420+ * @return 0 if valid, nonzero if invalid.
421+ */
422+ int fossil_media_json_validate (const char *json_text, fossil_media_json_error_t *err_out);
423+
424+ /* * @} */
425+
426+ /* * @name Path Access
427+ * @{
428+ */
429+
430+ /* *
431+ * @brief Get a JSON value using a dotted path expression.
432+ *
433+ * Supports object keys and array indices, e.g. "user.name" or "items[2].id".
434+ *
435+ * @param root Root JSON value.
436+ * @param path Path string (UTF-8, cannot be NULL).
437+ * @return Pointer to the JSON value, or NULL if not found.
438+ */
439+ fossil_media_json_value_t *
440+ fossil_media_json_get_path (const fossil_media_json_value_t *root, const char *path);
441+
442+ /* * @} */
443+
444+
253445#ifdef __cplusplus
254446}
255447#include < string>
@@ -437,6 +629,163 @@ namespace fossil {
437629 free (s);
438630 return result;
439631 }
632+
633+ /* *
634+ * @brief Deep copy this JSON value.
635+ * @return A new Json object that is a clone of this value.
636+ * @throws JsonError if cloning fails.
637+ */
638+ Json clone () const {
639+ fossil_media_json_value_t * v = fossil_media_json_clone (value_);
640+ if (!v) {
641+ throw JsonError (" Failed to clone JSON value" );
642+ }
643+ return Json (v);
644+ }
645+
646+ /* *
647+ * @brief Compare this JSON value to another for equality.
648+ * @param other The other Json object to compare.
649+ * @return true if equal, false otherwise.
650+ * @throws JsonError if comparison fails.
651+ */
652+ bool equals (const Json& other) const {
653+ int result = fossil_media_json_equals (value_, other.value_ );
654+ if (result == -1 ) {
655+ throw JsonError (" Failed to compare JSON values" );
656+ }
657+ return result == 1 ;
658+ }
659+
660+ /* *
661+ * @brief Check if this value is null.
662+ * @return true if null, false otherwise.
663+ */
664+ bool is_null () const {
665+ return fossil_media_json_is_null (value_) == 1 ;
666+ }
667+
668+ /* *
669+ * @brief Check if this value is an array.
670+ * @return true if array, false otherwise.
671+ */
672+ bool is_array () const {
673+ return fossil_media_json_is_array (value_) == 1 ;
674+ }
675+
676+ /* *
677+ * @brief Check if this value is an object.
678+ * @return true if object, false otherwise.
679+ */
680+ bool is_object () const {
681+ return fossil_media_json_is_object (value_) == 1 ;
682+ }
683+
684+ /* *
685+ * @brief Reserve capacity for a JSON array.
686+ * @param capacity Desired capacity.
687+ * @throws JsonError if not an array or reserve fails.
688+ */
689+ void array_reserve (size_t capacity) {
690+ if (fossil_media_json_array_reserve (value_, capacity) != 0 ) {
691+ throw JsonError (" Failed to reserve array capacity" );
692+ }
693+ }
694+
695+ /* *
696+ * @brief Reserve capacity for a JSON object.
697+ * @param capacity Desired capacity.
698+ * @throws JsonError if not an object or reserve fails.
699+ */
700+ void object_reserve (size_t capacity) {
701+ if (fossil_media_json_object_reserve (value_, capacity) != 0 ) {
702+ throw JsonError (" Failed to reserve object capacity" );
703+ }
704+ }
705+
706+ /* *
707+ * @brief Parse a JSON file into a Json object.
708+ * @param filename Path to JSON file.
709+ * @return Parsed Json object.
710+ * @throws JsonError if parsing fails.
711+ */
712+ static Json parse_file (const std::string& filename) {
713+ fossil_media_json_error_t err{};
714+ fossil_media_json_value_t * val = fossil_media_json_parse_file (filename.c_str (), &err);
715+ if (!val) {
716+ throw JsonError (std::string (" Parse file error: " ) + err.message );
717+ }
718+ return Json (val);
719+ }
720+
721+ /* *
722+ * @brief Write this JSON value to a file.
723+ * @param filename Path to output file.
724+ * @param pretty If true, output with indentation.
725+ * @throws JsonError if writing fails.
726+ */
727+ void write_file (const std::string& filename, bool pretty = false ) const {
728+ fossil_media_json_error_t err{};
729+ int rc = fossil_media_json_write_file (value_, filename.c_str (), pretty ? 1 : 0 , &err);
730+ if (rc != 0 ) {
731+ throw JsonError (std::string (" Write file error: " ) + err.message );
732+ }
733+ }
734+
735+ /* *
736+ * @brief Create a JSON integer value.
737+ * @param i Integer value.
738+ * @return Json object holding an integer.
739+ */
740+ static Json new_int (long long i) {
741+ return Json (fossil_media_json_new_int (i));
742+ }
743+
744+ /* *
745+ * @brief Get integer value from this JSON number.
746+ * @return Integer value.
747+ * @throws JsonError if not a number or out of range.
748+ */
749+ long long get_int () const {
750+ long long out = 0 ;
751+ if (fossil_media_json_get_int (value_, &out) != 0 ) {
752+ throw JsonError (" Failed to get integer from JSON value" );
753+ }
754+ return out;
755+ }
756+
757+ /* *
758+ * @brief Print a debug dump of this JSON value.
759+ * @param indent Starting indentation level.
760+ */
761+ void debug_dump (int indent = 0 ) const {
762+ fossil_media_json_debug_dump (value_, indent);
763+ }
764+
765+ /* *
766+ * @brief Validate JSON text without building a DOM.
767+ * @param text Input JSON text.
768+ * @return true if valid, false otherwise.
769+ */
770+ static bool validate (const std::string& text) {
771+ fossil_media_json_error_t err{};
772+ return fossil_media_json_validate (text.c_str (), &err) == 0 ;
773+ }
774+
775+ /* *
776+ * @brief Get a JSON value using a dotted path expression.
777+ * @param path Path string (e.g., "user.name" or "items[2].id").
778+ * @return Json object at the path.
779+ * @throws JsonError if not found.
780+ */
781+ Json get_path (const std::string& path) const {
782+ fossil_media_json_value_t * v = fossil_media_json_get_path (value_, path.c_str ());
783+ if (!v) {
784+ // Return a null Json object if path not found
785+ return Json (fossil_media_json_new_null ());
786+ }
787+ return Json (v);
788+ }
440789
441790 private:
442791 fossil_media_json_value_t * value_;
0 commit comments