diff --git a/src/data.zig b/src/data.zig index ecc9e35..9399c28 100644 --- a/src/data.zig +++ b/src/data.zig @@ -25,14 +25,14 @@ pub fn deinit() void { // Data ======================================================================= -pub const Data = struct { +pub const Frontmatter = struct { pub const Date = packed struct { year: c_int = 0, month: c_int = 0, day: c_int = 0, // needs format YYYY-MM-DD - pub fn parse(str: []const u8) !Data.Date { + pub fn parse(str: []const u8) !Frontmatter.Date { if (str.len != 10) return error.InvalidLength; return .{ @@ -49,8 +49,8 @@ pub const Data = struct { pub const KeyPairs = std.ArrayList(KV); /// parse if the first line == '+++' until first next line that == '+++' - pub fn parse(data_allocator: Allocator, reader: *Io.Reader) !Data.KeyPairs { - var props: Data.KeyPairs = try .initCapacity(data_allocator, 16); + pub fn parse(data_allocator: Allocator, reader: *Io.Reader) !Frontmatter.KeyPairs { + var props: Frontmatter.KeyPairs = try .initCapacity(data_allocator, 16); const first = reader.take(3) catch |e| switch (e) { error.EndOfStream => return error.NoFrontMatter, @@ -119,10 +119,10 @@ pub fn load() !void { }, else => return err, }; - try parse_dir(data_dir); + try parseDir(data_dir); } -fn parse_file(file: Io.File) !void { +fn parseFile(file: Io.File) !void { var real_path: [0x400]u8 = undefined; const i = try file.realPath(maivi.io, &real_path); const path = std.mem.cutPrefix(u8, real_path[0..i], maivi.config.root) orelse return error.Unexpected; @@ -134,105 +134,87 @@ fn parse_file(file: Io.File) !void { var a: std.heap.ArenaAllocator = .init(maivi.allocator); defer a.deinit(); const data_allocator = a.allocator(); - var data = Data.parse(data_allocator, reader) catch |err| switch (err) { - error.NoFrontMatter => return { - const l = maivi.lua.lua; - const stack_size_on_entry = l.getTop(); - - _ = l.getGlobal("this"); - _ = l.getField(-1, "data"); - - l.createTable(0, 0); - - const str = try maivi.allocator.dupeZ(u8, path); - defer maivi.allocator.free(str); - l.setField(-2, str); // pops data field table - - l.pop(2); // pops "data" and "this" - - if (stack_size_on_entry != l.getTop()) return error.UnexpectedStackSize; - }, + var data: ?Frontmatter.KeyPairs = Frontmatter.parse(data_allocator, reader) catch |err| switch (err) { + error.NoFrontMatter => null, else => return err, }; - defer data.deinit(data_allocator); + defer if (data != null) data.?.deinit(data_allocator); fern.debug().ctx("data.parse_file") .msg("adding data") .str("path", path).log(); - try add_data(maivi.lua.lua, path, data); + try setData(maivi.lua.lua, path, data); } -fn parse_dir(dir: Io.Dir) !void { +fn parseDir(dir: Io.Dir) !void { var it = dir.iterate(); return while (try it.next(maivi.io)) |x| switch (x.kind) { .file => { - const file = try dir.openFile(maivi.io, x.name, .{ .mode = .read_only }); - try parse_file(file); + const file = try dir.openFile(maivi.io, x.name, .{}); + try parseFile(file); }, - .directory => try parse_dir(try dir.openDir(maivi.io, x.name, .{ .iterate = true, .access_sub_paths = true })), + .directory => try parseDir(try dir.openDir(maivi.io, x.name, .{ .iterate = true, .access_sub_paths = true })), else => continue, }; } // lua ======================================================================== -pub fn add_data(l: *Lua, path: []const u8, d: Data.KeyPairs) !void { - const stack_size_on_entry = l.getTop(); - +pub fn setData(l: *Lua, path: []const u8, fmtable: ?Frontmatter.KeyPairs) !void { _ = l.getGlobal("this"); _ = l.getField(-1, "data"); + defer l.pop(2); // pops "data" and "this" + + _ = l.pushString(path); + if (fmtable) |t| + _ = try pushData(l, t) + else + l.newTable(); + l.setTable(-3); +} - l.createTable(0, @as(i32, @intCast(d.items.len))); - - _for: for (d.items) |kv| { - const stack_size_in_loop = l.getTop(); +// helpers ==================================================================== - if (kv.v == .str) - _ = l.pushString(kv.v.str) - else if (kv.v == .list) - try pushlist(l, kv.v.list) - else if (kv.v == .date) - try pushdate(l, kv.v.date) - else - continue :_for; +fn pushData(l: *Lua, d: Frontmatter.KeyPairs) !i32 { + l.createTable(0, @as(i32, @intCast(d.items.len))); - if (stack_size_in_loop + 1 != l.getTop()) return error.UnexpectedStackSize; + for (d.items) |kv| { + _ = l.pushString(kv.k); + errdefer l.pop(1); + defer l.setTable(-3); - const str = try maivi.allocator.dupeZ(u8, kv.k); - defer maivi.allocator.free(str); - l.setField(-2, str); + _ = switch (kv.v) { + .str => l.pushString(kv.v.str), + .list => try pushList(l, []const u8, kv.v.list), + .date => try pushDate(l, kv.v.date), + }; } - const str = try maivi.allocator.dupeZ(u8, path); - defer maivi.allocator.free(str); - l.setField(-2, str); // pops data field table - - l.pop(2); // pops "data" and "this" - - if (stack_size_on_entry != l.getTop()) return error.UnexpectedStackSize; + return 1; } -fn pushlist(l: *Lua, lst: [][]const u8) !void { - const stack_size_on_entry = l.getTop(); - +/// * Pops from Stack: `0` +/// * Pushes to Stack: `1` +fn pushList(l: *Lua, comptime T: anytype, lst: []T) !i32 { l.createTable(@as(i32, @intCast(lst.len)), 0); - + errdefer l.pop(1); for (lst, 1..lst.len + 1) |item, n| { - _ = l.pushString(item); + _ = try l.pushAny(item); l.setIndexRaw(-2, @as(c_int, @intCast(n))); } - - if (stack_size_on_entry + 1 != l.getTop()) return error.UnexpectedStackSize; + return 1; } -fn pushdate(l: *Lua, date: Data.Date) !void { - const stack_size_on_entry = l.getTop(); +/// * Pops from Stack: `0` +/// * Pushes to Stack: `1` +fn pushDate(l: *Lua, date: Frontmatter.Date) !i32 { _ = l.getGlobal("os"); _ = l.getField(-1, "time"); - l.remove(stack_size_on_entry + 1); + l.remove(l.absIndex(-2)); if (!l.isFunction(-1)) return error.Unexpected; + // { year = ..., month = ..., day = ... } l.createTable(0, 3); l.pushInteger(date.year); l.setField(-2, "year"); @@ -242,6 +224,5 @@ fn pushdate(l: *Lua, date: Data.Date) !void { l.setField(-2, "day"); try lua.pcall(l, .{ .args = 1, .results = 1 }); - - if (stack_size_on_entry + 1 != l.getTop()) return error.UnexpectedStackSize; + return 1; }