pub const c = @cImport({ @cInclude("json.h"); }); const std = @import("std"); const Type = enum(c.json_type) { null = c.json_type_null, boolean = c.json_type_boolean, double = c.json_type_double, int = c.json_type_int, object = c.json_type_object, array = c.json_type_array, string = c.json_type_string, }; pub const Obj = struct { // Option because a null value is treated as the C value NULL in json-c obj: ?*c.json_object, /////////////////////////////////// // ** Object creation ** ///////////////////////////////// pub fn newObject() Obj { return Obj{ .obj = c.json_object_new_object().? }; } pub fn newArray() Obj { return Obj{ .obj = c.json_object_new_array().? }; } pub fn newString(s: [*c]const u8) Obj { return Obj{ .obj = c.json_object_new_string(s).? }; } pub fn newInt32(i: i32) Obj { return Obj{ .obj = c.json_object_new_int(i).? }; } pub fn newInt64(i: i64) Obj { return Obj{ .obj = c.json_object_new_int64(i).? }; } pub fn newFromString(s: [*c]const u8) !Obj { const FromStringError = error{ MalformedString, }; return Obj{ .obj = c.json_tokener_parse(s) orelse return FromStringError.MalformedString, }; } pub fn deinit(self: *Obj) void { _ = c.json_object_put(self.obj); } /////////////////////////////////// // ** General functions ** ///////////////////////////////// const TypeError = error{ TypeMismatch, // Tried to use a function on the wrong JSON format }; pub fn getType(self: Obj) Type { //std.debug.print("{}\n", .{c.json_object_get_type(self.obj)}); return @intToEnum(Type, c.json_object_get_type(self.obj)); } // Not *const Obj because the json_object retains ownership of the string pub fn toString(self: *const Obj) []const u8 { // TODO: Allow passing of flags as an enum like the SDL2 binding return std.mem.sliceTo(c.json_object_to_json_string(self.obj), 0); } /////////////////////////////////// // ** Getters ** ///////////////////////////////// // Not *const Obj because the json_object retains ownership of the string pub fn getString(self: *const Obj) []const u8 { // TODO: Check type to not allow other types return std.mem.sliceTo(c.json_object_get_string(self.obj), 0); } pub fn getInt64(self: Obj) i64 { // TODO: Check ERRNO to see if there was an error return c.json_object_get_int64(self.obj); } /////////////////////////////////// // ** Object functions ** ///////////////////////////////// const GetError = error { TypeMismatch, // Not an object/array KeyNotFound, // Key or ID not present in the object/array }; pub fn objectGet(self: *const Obj, key: [*c]const u8) GetError!Obj { // TODO: Check type and null return as errors var obj: ?*c.json_object = undefined; const succ = c.json_object_object_get_ex(self.obj, key, &obj); // Everything went alright if (obj) |ret| { return Obj{ .obj = c.json_object_get(ret).? }; } else if (obj == null and succ != 0) { // The actual value is just null return Obj{ .obj = null }; } else if (self.getType() != Type.object) { return GetError.TypeMismatch; } else { return GetError.KeyNotFound; } } pub fn objectAdd(self: *Obj, key: [*c]const u8, value: ?*const Obj) void { // TODO: Check type and error return // We need the json-c object or null, not the zig object const o = if (value) |i| i.obj else null; _ = c.json_object_object_add(self.obj, key, o); } pub fn objectAddString(self: *Obj, key: [*c]const u8, value: [*c]const u8) void { _ = c.json_object_object_add(self.obj, key, newString(value).obj); } pub fn objectLen(self: *const Obj) c_int { return c.json_object_object_length(self.obj); } pub fn objectGetIterator(self: *Obj) ObjectIterator { // TODO: Check errors and check it is a JSON Object return ObjectIterator{ .it = c.json_object_iter_begin(self.obj), .end = c.json_object_iter_end(self.obj), }; } pub const ObjectIterator = struct { it: c.json_object_iterator, end: c.json_object_iterator, const ReturnType = struct { key: []const u8, value: ?Obj, }; // These function's names are way too long ._. const step = c.json_object_iter_next; const equal = c.json_object_iter_equal; const peekName = c.json_object_iter_peek_name; const peekValue = c.json_object_iter_peek_value; /// Step the iterator. The owner mustn't call deinit() on the returned value. pub fn next(self: *ObjectIterator) ?ReturnType { const it = &self.it; const end = &self.end; // Reached the end if (equal(it, end) != 0) return null; defer step(it); return ReturnType{ .key = std.mem.sliceTo(peekName(it), 0), .value = if (peekValue(it)) |val| Obj{ .obj = val } else null, }; } }; /////////////////////////////////// // ** Array functions ** ///////////////////////////////// pub fn arrayAdd(self: *Obj, value: ?*const Obj) void { // TODO: Check type and error return // We need the json-c object or null, not the zig object const o = if (value) |i| i.obj else null; _ = c.json_object_array_add(self.obj, o); } pub fn arrayLen(self: Obj) usize { return c.json_object_array_length(self.obj); } pub fn arrayGet(self: *const Obj, idx: usize) ?Obj { // Sould the .? be there? if (c.json_object_array_get_idx(self.obj, idx)) |obj| { return Obj{ .obj = c.json_object_get(obj).? }; } else { return null; } } pub fn arrayGetIterator(self: *const Obj) ArrayIterator { return ArrayIterator{ .obj = self, .idx = 0 }; } pub const ArrayIterator = struct { obj: *const Obj, idx: usize = 0, pub fn next(self: *ArrayIterator) ?Obj { defer self.idx += 1; return self.obj.arrayGet(self.idx); } }; };