const asynk = @import("async"); const std = @import("std"); const utils = @import("utils.zig"); const zml = @import("../zml.zig"); const StringBuilder = std.ArrayListUnmanaged(u8); const Allocator = std.mem.Allocator; pub fn open(allocator: std.mem.Allocator, path: []const u8) !zml.aio.BufferStore { const file = try std.fs.cwd().openFile(path, .{}); defer file.close(); var res: zml.aio.BufferStore = .{ .arena = std.heap.ArenaAllocator.init(allocator), }; errdefer res.arena.deinit(); const arena = res.arena.allocator(); const json_data = try file.reader().readAllAlloc(arena, (try file.metadata()).size()); const metadata = try std.json.parseFromSliceLeaky(std.json.Value, allocator, json_data, .{ .allocate = .alloc_if_needed }); var it = metadata.object.iterator(); while (it.next()) |entry| { var prefix_buf: [1024]u8 = undefined; try parseMetadata(allocator, &res, StringBuilder.initBuffer(&prefix_buf), entry.value_ptr.*); } return res; } pub fn parseMetadata(allocator: Allocator, store: *zml.aio.BufferStore, prefix: StringBuilder, val: std.json.Value) !void { const metadata = &store._metadata; const key = prefix.items; return switch (val) { .null => try metadata.put(allocator, try allocator.dupe(u8, key), .{ .null = {} }), .bool => |v| try metadata.put(allocator, try allocator.dupe(u8, key), .{ .boolval = v }), .integer => |v| try metadata.put(allocator, try allocator.dupe(u8, key), .{ .int64 = v }), .float => |v| try metadata.put(allocator, try allocator.dupe(u8, key), .{ .float64 = v }), .number_string, .string => |v| try metadata.put(allocator, try allocator.dupe(u8, key), .{ .string = try allocator.dupe(u8, v) }), .array => |v| { if (v.items.len == 0) return; return if (validSlice(v)) |item_type| { const data, const dtype: zml.aio.Value.Slice.ItemType = switch (item_type) { .bool => blk: { const values = try allocator.alloc(bool, v.items.len); for (v.items, 0..) |item, i| values[i] = item.bool; break :blk .{ std.mem.sliceAsBytes(values), .boolval }; }, .integer => blk: { const values = try allocator.alloc(i64, v.items.len); for (v.items, 0..) |item, i| values[i] = item.integer; break :blk .{ std.mem.sliceAsBytes(values), .int64 }; }, .float => blk: { const values = try allocator.alloc(f64, v.items.len); for (v.items, 0..) |item, i| values[i] = item.float; break :blk .{ std.mem.sliceAsBytes(values), .float64 }; }, inline .string, .number_string => |tag| blk: { const values = try allocator.alloc([]const u8, v.items.len); for (v.items, 0..) |item, i| { values[i] = @field(item, @tagName(tag)); } break :blk .{ std.mem.sliceAsBytes(values), .string }; }, .null, .array, .object => unreachable, }; try metadata.put( allocator, try allocator.dupe(u8, key), .{ .array = .{ .item_type = dtype, .data = data } }, ); } else { for (v.items, 0..) |item, i| { var new_prefix = prefix; if (prefix.items.len > 0) new_prefix.appendAssumeCapacity('.'); new_prefix.items.len += std.fmt.formatIntBuf(new_prefix.unusedCapacitySlice(), i, 10, .lower, .{}); try parseMetadata(allocator, store, new_prefix, item); } }; }, .object => |v| { var obj_iter = v.iterator(); while (obj_iter.next()) |entry| { var new_prefix = prefix; if (prefix.items.len > 0) new_prefix.appendAssumeCapacity('.'); new_prefix.appendSliceAssumeCapacity(entry.key_ptr.*); try parseMetadata(allocator, store, new_prefix, entry.value_ptr.*); } }, }; } /// We can only create a Zig slice out of json array, if all values /// in the array have the same type. fn validSlice(v: std.json.Array) ?std.meta.Tag(std.json.Value) { if (v.items.len == 0) return null; const item_type: std.meta.Tag(std.json.Value) = v.items[0]; switch (item_type) { .null, .array, .object => return null, else => {}, } for (v.items[1..]) |item| { if (item != item_type) return null; } return item_type; }