From 3d0ef52feda18dbb0a40a97052da6492d6e1a735 Mon Sep 17 00:00:00 2001 From: robin Date: Sat, 20 Jun 2026 10:26:48 +0200 Subject: [PATCH] refactor: restructure modules --- justfile | 3 +- src/data.zig | 171 ++++++++++--------- src/gen.zig | 2 +- src/handler.zig | 2 +- src/lua.zig | 282 -------------------------------- src/{ => lua}/embed.zig | 0 src/{ => lua}/lua_display.zig | 5 +- src/lua/lua_print.zig | 63 +++++++ src/lua/lua_read.zig | 19 +++ src/lua/lua_require.zig | 85 ++++++++++ src/lua/pcall.zig | 29 ++++ src/lua/root.zig | 120 ++++++++++++++ src/{ => lua}/runtime/_meta.lua | 4 + src/{ => lua}/runtime/h.lua | 0 src/{ => lua}/runtime/iter.lua | 0 src/{ => lua}/runtime/maivi.lua | 0 src/{ => lua}/runtime/this.lua | 0 src/root.zig | 35 ++-- src/router.zig | 19 ++- test.lua | 2 +- 20 files changed, 450 insertions(+), 391 deletions(-) delete mode 100644 src/lua.zig rename src/{ => lua}/embed.zig (100%) rename src/{ => lua}/lua_display.zig (98%) create mode 100644 src/lua/lua_print.zig create mode 100644 src/lua/lua_read.zig create mode 100644 src/lua/lua_require.zig create mode 100644 src/lua/pcall.zig create mode 100644 src/lua/root.zig rename src/{ => lua}/runtime/_meta.lua (89%) rename src/{ => lua}/runtime/h.lua (100%) rename src/{ => lua}/runtime/iter.lua (100%) rename src/{ => lua}/runtime/maivi.lua (100%) rename src/{ => lua}/runtime/this.lua (100%) diff --git a/justfile b/justfile index 42fb669..9efa21c 100644 --- a/justfile +++ b/justfile @@ -24,7 +24,8 @@ watch: [private] zig-build *args: - zig build {{args}} \ + @printf "zig build %s\n" "{{args}}" + @zig build {{args}} \ |& hl -P --config=hl.toml [private] diff --git a/src/data.zig b/src/data.zig index 70c1c3b..dc013f4 100644 --- a/src/data.zig +++ b/src/data.zig @@ -2,12 +2,14 @@ const std = @import("std"); const Io = std.Io; const Allocator = std.mem.Allocator; +const fern = @import("fern"); +const zlua = @import("zlua"); +const Lua = zlua.Lua; + const maivi = @import("root.zig"); const lua = maivi.lua; -const fern = @import("fern"); - -const zlua = @import("zlua"); +// init ======================================================================= var arena: std.heap.ArenaAllocator = undefined; var allocator: Allocator = undefined; @@ -21,66 +23,92 @@ pub fn deinit() void { arena.deinit(); } -pub const Date = packed struct { - year: c_int = 0, - month: c_int = 0, - day: c_int = 0, -}; +// Data ======================================================================= -const DataT = union(enum) { str: []const u8, date: Date, list: [][]const u8 }; -const DataKV = struct { k: []const u8, v: DataT }; -const DataL = std.ArrayList(DataKV); -/// parse if the first line == '+++' until first next line that == '+++' -fn parse_data(data_allocator: Allocator, reader: *Io.Reader) !DataL { - var props: DataL = try .initCapacity(data_allocator, 16); - - const first = try reader.take(3); - if (!std.mem.eql(u8, first, "+++")) return error.NoFrontMatter; - - w: while (true) { - const line = reader.takeDelimiterExclusive('\n') catch break :w; - p: { - _ = reader.peek(1) catch break :p; - reader.toss(1); - } - if (std.mem.eql(u8, line, "+++")) break :w; - - var it = std.mem.splitScalar(u8, line, '='); - const key = std.mem.trim(u8, it.first(), " "); - const value = if (it.peek()) |v| - std.mem.trim(u8, v, " ") - else - continue :w; +pub const Data = struct { + pub const Date = packed struct { + year: c_int = 0, + month: c_int = 0, + day: c_int = 0, - if (value.len == 0) continue :w; + // needs format YYYY-MM-DD + pub fn parse(str: []const u8) !Data.Date { + if (str.len != 10) return error.InvalidLength; - if (value[0] == '"') { - if (value[value.len - 1] != '"') continue :w; - const str = value[1 .. value.len - 1]; - try props.append(data_allocator, .{ .k = key, .v = .{ .str = str } }); - continue :w; + return .{ + .year = try std.fmt.parseInt(c_int, str[0..4], 10), + .month = try std.fmt.parseInt(c_int, str[5..7], 10), + .day = try std.fmt.parseInt(c_int, str[8..10], 10), + }; } - if (std.mem.findScalar(u8, value, ',')) |_| { - var list: std.ArrayList([]const u8) = try .initCapacity(data_allocator, 16); - - var l_it = std.mem.splitScalar(u8, value, ','); - while (l_it.next()) |item| if (item.len > 0) try list.append(data_allocator, item); + }; - try props.append(data_allocator, .{ .k = key, .v = .{ .list = try list.toOwnedSlice(data_allocator) } }); - continue :w; + pub const Item = union(enum) { str: []const u8, date: Date, list: [][]const u8 }; + + const KV = struct { k: []const u8, v: Item }; + 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); + + const first = try reader.take(3); + if (!std.mem.eql(u8, first, "+++")) return error.NoFrontMatter; + + w: while (true) { + const line = reader.takeDelimiterExclusive('\n') catch break :w; + p: { + _ = reader.peek(1) catch break :p; + reader.toss(1); + } + if (std.mem.eql(u8, line, "+++")) break :w; + + var it = std.mem.splitScalar(u8, line, '='); + const key = std.mem.trim(u8, it.first(), " "); + const value = if (it.peek()) |v| + std.mem.trim(u8, v, " ") + else + continue :w; + + if (value.len == 0) continue :w; + + if (value[0] == '"') { + if (value[value.len - 1] != '"') continue :w; + const str = value[1 .. value.len - 1]; + try props.append(data_allocator, .{ .k = key, .v = .{ .str = str } }); + continue :w; + } + if (std.mem.findScalar(u8, value, ',')) |_| { + var list: std.ArrayList([]const u8) = try .initCapacity(data_allocator, 16); + + var l_it = std.mem.splitScalar(u8, value, ','); + while (l_it.next()) |item| if (item.len > 0) try list.append(data_allocator, item); + + try props.append(data_allocator, .{ .k = key, .v = .{ .list = try list.toOwnedSlice(data_allocator) } }); + continue :w; + } + + const date = Date.parse(value) catch |err| { + fern.err().ctx("data.Data.parse") + .msg("date parse failed") + .str("v", value) + .err(err).log(); + continue :w; + }; + try props.append(data_allocator, .{ .k = key, .v = .{ .date = date } }); } - const date = parse_date(value) catch |err| { - fern.err().ctx("data.parse_data") - .msg("date parse failed") - .str("v", value) - .err(err).log(); - continue :w; - }; - try props.append(data_allocator, .{ .k = key, .v = .{ .date = date } }); + return props; } +}; - return props; +// load ======================================================================= + +pub fn load() !void { + fern.trace().ctx("data.load").log(); + const rootdir = try Io.Dir.openDirAbsolute(maivi.io, maivi.config.root, .{}); + const data_dir = try Io.Dir.openDir(rootdir, maivi.io, "data", .{ .iterate = true, .access_sub_paths = true }); + try parse_dir(data_dir); } fn parse_file(file: Io.File) !void { @@ -95,7 +123,7 @@ 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 = parse_data(data_allocator, reader) catch |err| switch (err) { + var data = Data.parse(data_allocator, reader) catch |err| switch (err) { error.NoFrontMatter => return, else => return err, }; @@ -120,14 +148,9 @@ fn parse_dir(dir: Io.Dir) !void { }; } -pub fn load_data() !void { - fern.trace().ctx("data.load_data").log(); - const rootdir = try Io.Dir.openDirAbsolute(maivi.io, maivi.config.root, .{}); - const data_dir = try Io.Dir.openDir(rootdir, maivi.io, "data", .{ .iterate = true, .access_sub_paths = true }); - try parse_dir(data_dir); -} +// lua ======================================================================== -pub fn add_data(l: *zlua.Lua, path: []const u8, d: DataL) !void { +pub fn add_data(l: *Lua, path: []const u8, d: Data.KeyPairs) !void { const stack_size_on_entry = l.getTop(); _ = l.getGlobal("this"); @@ -147,7 +170,7 @@ pub fn add_data(l: *zlua.Lua, path: []const u8, d: DataL) !void { else continue :_for; - if (stack_size_in_loop + 1 != l.getTop()) return error.Unexpected; + if (stack_size_in_loop + 1 != l.getTop()) return error.UnexpectedStackSize; const str = try maivi.allocator.dupeZ(u8, kv.k); defer maivi.allocator.free(str); @@ -160,10 +183,10 @@ pub fn add_data(l: *zlua.Lua, path: []const u8, d: DataL) !void { l.pop(2); // pops "data" and "this" - if (stack_size_on_entry != l.getTop()) return error.Unexpected; + if (stack_size_on_entry != l.getTop()) return error.UnexpectedStackSize; } -fn pushlist(l: *zlua.Lua, lst: [][]const u8) !void { +fn pushlist(l: *Lua, lst: [][]const u8) !void { const stack_size_on_entry = l.getTop(); l.createTable(@as(i32, @intCast(lst.len)), 0); @@ -173,10 +196,10 @@ fn pushlist(l: *zlua.Lua, lst: [][]const u8) !void { l.setIndexRaw(-2, @as(c_int, @intCast(n))); } - if (stack_size_on_entry + 1 != l.getTop()) return error.Unexpected; + if (stack_size_on_entry + 1 != l.getTop()) return error.UnexpectedStackSize; } -fn pushdate(l: *zlua.Lua, date: Date) !void { +fn pushdate(l: *Lua, date: Data.Date) !void { const stack_size_on_entry = l.getTop(); _ = l.getGlobal("os"); _ = l.getField(-1, "time"); @@ -193,17 +216,5 @@ fn pushdate(l: *zlua.Lua, date: Date) !void { try lua.pcall(l, .{ .args = 1, .results = 1 }); - if (stack_size_on_entry + 1 != l.getTop()) return error.Unexpected; -} - -// needs format YYYY-MM-DD -fn parse_date(str: []const u8) !Date { - if (str.len != 10) return error.InvalidLength; - - var date: Date = .{}; - date.year = try std.fmt.parseInt(c_int, str[0..4], 10); - date.month = try std.fmt.parseInt(c_int, str[5..7], 10); - date.day = try std.fmt.parseInt(c_int, str[8..10], 10); - - return date; + if (stack_size_on_entry + 1 != l.getTop()) return error.UnexpectedStackSize; } diff --git a/src/gen.zig b/src/gen.zig index efb8461..3a77d82 100644 --- a/src/gen.zig +++ b/src/gen.zig @@ -6,7 +6,7 @@ const maivi = @import("root.zig"); const fern = @import("fern"); const zlua = @import("zlua"); -const lua = @import("lua.zig"); +const lua = maivi.lua; var arena: std.heap.ArenaAllocator = undefined; var allocator: std.mem.Allocator = undefined; diff --git a/src/handler.zig b/src/handler.zig index d581cd4..73c368a 100644 --- a/src/handler.zig +++ b/src/handler.zig @@ -8,7 +8,7 @@ const httpz = @import("httpz"); const helpers = @import("helpers.zig"); const Status = helpers.Status; -const lua = @import("lua.zig"); +const lua = maivi.lua; const Self = @This(); diff --git a/src/lua.zig b/src/lua.zig deleted file mode 100644 index 79f4d7c..0000000 --- a/src/lua.zig +++ /dev/null @@ -1,282 +0,0 @@ -const std = @import("std"); -const maivi = @import("root.zig"); - -const fern = @import("fern"); - -const zlua = @import("zlua"); -pub const Lua = zlua.Lua; - -const opts = @import("opts"); - -pub const data = @import("data.zig"); - -const helpers = maivi.helpers; - -pub var lua: *Lua = undefined; - -// var arena: std.heap.ArenaAllocator = undefined; -// var chunk_allocator: std.mem.Allocator = undefined; -// var packages: std.StringHashMap([]const u8) = undefined; - -const embed = @import("embed.zig"); -const lua_display = @import("lua_display.zig"); - -pub fn init() !void { - fern.trace().ctx("lua.init").log(); - - // arena = .init(maivi.allocator); - // errdefer arena.deinit(); - // chunk_allocator = arena.allocator(); - // packages = .init(chunk_allocator); - - lua = try lua_init(); -} - -pub fn lua_init() !*Lua { - const l = try Lua.init(maivi.allocator); - errdefer l.deinit(); - l.openBase(); - l.openOS(); - l.openDebug(); - l.openString(); - l.openTable(); - l.openMath(); - l.openBit32(); - - // package ================================================================ - - l.openPackage(); - - _ = l.getGlobal("package"); - - const path = try std.fmt.allocPrint(maivi.allocator, "{s}/?.lua;{s}/?/init.lua", .{ maivi.config.luaroot, maivi.config.luaroot }); - defer maivi.allocator.free(path); - _ = l.pushString(path); - l.setField(-2, "path"); - - l.pop(1); // pops "package" - - // global fn ============================================================== - - l.register("print", zlua.wrap(lua_print)); - // l.register("require", zlua.wrap(lua_require)); - l.register("read", zlua.wrap(lua_read)); - - // maivi ====================================================================== - - try l.loadBuffer(embed.maivi_module, "@runtime/maivi.lua"); - try pcall(l, .{ .results = 1 }); - l.setGlobal("maivi"); - - // h ========================================================================== - - try l.loadBuffer(embed.h_module, "@runtime/h.lua"); - try pcall(l, .{ .results = 1 }); - l.setGlobal("h"); - - // this ======================================================================= - - try l.loadBuffer(embed.this_module, "@runtime/this.lua"); - try pcall(l, .{ .results = 1 }); - - _ = l.pushString(maivi.config.base_url); - l.setField(-2, "base_url"); - _ = l.pushString(opts.version); - l.setField(-2, "rev"); - - l.setGlobal("this"); - - // iter ======================================================================= - - try l.loadBuffer(embed.iter_module, "@runtime/iter.lua"); - try pcall(l, .{ .results = 1 }); - l.setGlobal("Iter"); - - // display ==================================================================== - - _ = l.getGlobal("maivi"); - _ = try lua_display.lua_opendisplay(l); - l.setField(-2, "display"); - - l.createTable(0, 1); - l.pushFunction(zlua.wrap(maivi.Router.lua_add)); - l.setField(-2, "add"); - l.setField(-2, "router"); - - l.pop(1); // pops "maivi" - - if (l.getTop() != 0) return error.Unexpected; - - return l; -} - -pub fn deinit() void { - lua.deinit(); - // arena.deinit(); -} - -/// * Pops from Stack: `0` -/// * Pushes to Stack: `1` -fn do_require(l: *Lua, modname: []const u8) !void { - _ = l.getGlobal("require"); - _ = l.pushString(modname); - try pcall(l, .{ .args = 1, .results = 1 }); -} - -pub fn setup() !void { - // explicitly import `init.lua` - try do_require(lua, "init"); - if (lua.isNil(-1) or !lua.isFunction(-1)) { - fern.err().msg("lua/init.lua does not return an init function").log(); - return error.Unexpected; - } - try pcall(lua, .{ .args = 0, .results = 0 }); -} - -/// * Pops from Stack: `(args.args + 1)` -/// * Pushes to Stack: `(args.results|1)` -pub fn pcall(l: *Lua, args: Lua.ProtectedCallArgs) !void { - return l.protectedCall(args) catch |err| switch (err) { - error.LuaRuntime => { - const msg = try l.toString(-1); - l.pop(1); - l.traceback(l, msg, 1); - const str = try l.toString(-1); - l.pop(1); - const e = try maivi.allocator.dupe(u8, str); - defer maivi.allocator.free(e); - fern.err().ctx("lua.pcall").str("err", e).log(); - return err; - }, - else => { - fern.err().ctx("lua.pcall").err(err).log(); - return err; - }, - }; -} - -fn get_keys(l: *zlua.Lua, keys: *std.ArrayList([]const u8)) !void { - l.pushValue(-1); - - if (!l.isTable(-1)) return error.NotATable; - - l.pushNil(); - // stack now contains: -1 => nil; -2 => table - while (l.next(-2)) { - // stack now contains: -1 => value; -2 => key; -3 => table - // copy the key so that toString does not modify the original - l.pushValue(-2); - // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table - const kz = try l.toString(-1); - keys.appendBounded(kz) catch continue; - // pop value + copy of key, leaving original key - l.pop(2); - // stack now contains: -1 => key; -2 => table - } - - l.pop(1); -} - -fn lua_print(l: *Lua) !i32 { - var logger = fern.info().ctx("lua.print"); - defer logger.log(); - - l.pushValue(-1); - - if (l.isTable(-1)) { - l.pushNil(); - // stack now contains: -1 => nil; -2 => table - while (l.next(-2)) { - // stack now contains: -1 => value; -2 => key; -3 => table - // copy the key so that toString does not modify the original - l.pushValue(-2); - // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table - const kz = try l.toString(-1); - const vz = l.toString(-2) catch vz: { - break :vz l.typeNameIndex(-2); - }; - logger = logger.str(kz, vz); - // pop value + copy of key, leaving original key - l.pop(2); - // stack now contains: -1 => key; -2 => table - } - } else { - const str = try l.toString(-1); - l.pop(1); - const s = try maivi.allocator.dupe(u8, str); - defer maivi.allocator.free(s); - logger = logger.str("v", s); - } - - l.pop(1); - - return 0; -} - -fn lua_read(l: *Lua) !i32 { - const path = try l.toString(-1); - l.pop(1); - - const dir = try std.Io.Dir.openDirAbsolute(maivi.io, maivi.config.root, .{}); - const contents = try helpers.read_file(maivi.allocator, dir, path); - defer maivi.allocator.free(contents); - _ = l.pushString(contents); - - return 1; -} - -// fn loadchunk(l: *Lua, modname: []const u8) !void { -// const buffer = packages.get(modname) orelse return error.PackageNotFound; -// -// const modnameZ = try maivi.allocator.dupeZ(u8, modname); -// defer maivi.allocator.free(modnameZ); -// -// fern.debug().ctx("lua.loadchunk") -// .msg("loading buffer") -// .str("modname", modname).log(); -// try l.loadBuffer(buffer, modnameZ); -// try pcall(l, .{ .args = 0, .results = 1 }); -// } -// -// fn loadfile(modname: []const u8) !void { -// const dir = try std.Io.Dir.openDirAbsolute(maivi.io, maivi.config.luaroot, .{}); -// const contents = helpers.read_file(chunk_allocator, dir, modname) catch |err| switch (err) { -// error.IsDirectory => blk: { -// const new_path = try std.mem.concat(maivi.allocator, u8, &.{ modname, "/init.lua" }); -// defer maivi.allocator.free(new_path); -// break :blk try helpers.read_file(chunk_allocator, dir, new_path); -// }, -// error.FileNotFound => blk: { -// const new_path = try std.mem.concat(maivi.allocator, u8, &.{ modname, ".lua" }); -// defer maivi.allocator.free(new_path); -// break :blk try helpers.read_file(chunk_allocator, dir, new_path); -// }, -// else => return err, -// }; -// errdefer chunk_allocator.free(contents); -// -// fern.debug().ctx("lua.loadfile") -// .msg("saving contents") -// .str("modname", modname).log(); -// const modnameZ = try maivi.allocator.dupeZ(u8, modname); -// defer maivi.allocator.free(modnameZ); -// -// if (packages.contains(modname)) return error.Clobber; -// try packages.put(modname, contents); -// } -// -// pub fn lua_require(l: *Lua) !i32 { -// const modname = try l.toString(-1); -// l.pop(1); -// fern.debug().ctx("lua.require").str("modname", modname).log(); -// -// loadchunk(l, modname) catch |err| switch (err) { -// error.PackageNotFound => { -// try loadfile(modname); -// try loadchunk(l, modname); -// }, -// else => return err, -// }; -// -// return 1; -// } diff --git a/src/embed.zig b/src/lua/embed.zig similarity index 100% rename from src/embed.zig rename to src/lua/embed.zig diff --git a/src/lua_display.zig b/src/lua/lua_display.zig similarity index 98% rename from src/lua_display.zig rename to src/lua/lua_display.zig index 5ab3954..8eee8ff 100644 --- a/src/lua_display.zig +++ b/src/lua/lua_display.zig @@ -1,12 +1,11 @@ const std = @import("std"); -const maivi = @import("root.zig"); - const fern = @import("fern"); - const zlua = @import("zlua"); const Lua = zlua.Lua; +const maivi = @import("../root.zig"); + pub fn lua_opendisplay(l: *Lua) !i32 { l.createTable(0, 2); diff --git a/src/lua/lua_print.zig b/src/lua/lua_print.zig new file mode 100644 index 0000000..661ce1b --- /dev/null +++ b/src/lua/lua_print.zig @@ -0,0 +1,63 @@ +const fern = @import("fern"); +const zlua = @import("zlua"); +pub const Lua = zlua.Lua; + +const maivi = @import("../root.zig"); + +pub fn lua_print(l: *Lua) !i32 { + var logger = fern.info().ctx("lua.print"); + defer logger.log(); + + l.pushValue(-1); + + if (l.isTable(-1)) { + l.pushNil(); + // stack now contains: -1 => nil; -2 => table + while (l.next(-2)) { + // stack now contains: -1 => value; -2 => key; -3 => table + // copy the key so that toString does not modify the original + l.pushValue(-2); + // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table + const kz = try l.toString(-1); + const vz = l.toString(-2) catch vz: { + break :vz l.typeNameIndex(-2); + }; + logger = logger.str(kz, vz); + // pop value + copy of key, leaving original key + l.pop(2); + // stack now contains: -1 => key; -2 => table + } + } else { + const str = try l.toString(-1); + l.pop(1); + const s = try maivi.allocator.dupe(u8, str); + defer maivi.allocator.free(s); + logger = logger.str("v", s); + } + + l.pop(1); + + return 0; +} + +// fn get_keys(l: *zlua.Lua, keys: *std.ArrayList([]const u8)) !void { +// l.pushValue(-1); +// +// if (!l.isTable(-1)) return error.NotATable; +// +// l.pushNil(); +// // stack now contains: -1 => nil; -2 => table +// while (l.next(-2)) { +// // stack now contains: -1 => value; -2 => key; -3 => table +// // copy the key so that toString does not modify the original +// l.pushValue(-2); +// // stack now contains: -1 => key; -2 => value; -3 => key; -4 => table +// const kz = try l.toString(-1); +// keys.appendBounded(kz) catch continue; +// // pop value + copy of key, leaving original key +// l.pop(2); +// // stack now contains: -1 => key; -2 => table +// } +// +// l.pop(1); +// } diff --git a/src/lua/lua_read.zig b/src/lua/lua_read.zig new file mode 100644 index 0000000..02c91f4 --- /dev/null +++ b/src/lua/lua_read.zig @@ -0,0 +1,19 @@ +const std = @import("std"); + +const zlua = @import("zlua"); +pub const Lua = zlua.Lua; + +const maivi = @import("../root.zig"); +const helpers = maivi.helpers; + +pub fn lua_read(l: *Lua) !i32 { + const path = try l.toString(-1); + l.pop(1); + + const dir = try std.Io.Dir.openDirAbsolute(maivi.io, maivi.config.root, .{}); + const contents = try helpers.read_file(maivi.allocator, dir, path); + defer maivi.allocator.free(contents); + _ = l.pushString(contents); + + return 1; +} diff --git a/src/lua/lua_require.zig b/src/lua/lua_require.zig new file mode 100644 index 0000000..5540091 --- /dev/null +++ b/src/lua/lua_require.zig @@ -0,0 +1,85 @@ +// NOTE: just use package lib for luajit + +const std = @import("std"); + +const fern = @import("fern"); +const opts = @import("opts"); +const zlua = @import("zlua"); +pub const Lua = zlua.Lua; + +pub const data = @import("data.zig"); +const embed = @import("embed.zig"); +const lua_display = @import("lua_display.zig"); +const maivi = @import("../root.zig"); +const helpers = maivi.helpers; + +var arena: std.heap.ArenaAllocator = undefined; +var chunk_allocator: std.mem.Allocator = undefined; +var packages: std.StringHashMap([]const u8) = undefined; + +pub fn init() !void { + arena = .init(maivi.allocator); + errdefer arena.deinit(); + chunk_allocator = arena.allocator(); + packages = .init(chunk_allocator); +} + +pub fn deinit() void { + arena.deinit(); +} + +fn loadchunk(l: *Lua, modname: []const u8) !void { + const buffer = packages.get(modname) orelse return error.PackageNotFound; + + const modnameZ = try maivi.allocator.dupeZ(u8, modname); + defer maivi.allocator.free(modnameZ); + + fern.debug().ctx("lua.loadchunk") + .msg("loading buffer") + .str("modname", modname).log(); + try l.loadBuffer(buffer, modnameZ); + try pcall(l, .{ .args = 0, .results = 1 }); +} + +fn loadfile(modname: []const u8) !void { + const dir = try std.Io.Dir.openDirAbsolute(maivi.io, maivi.config.luaroot, .{}); + const contents = helpers.read_file(chunk_allocator, dir, modname) catch |err| switch (err) { + error.IsDirectory => blk: { + const new_path = try std.mem.concat(maivi.allocator, u8, &.{ modname, "/init.lua" }); + defer maivi.allocator.free(new_path); + break :blk try helpers.read_file(chunk_allocator, dir, new_path); + }, + error.FileNotFound => blk: { + const new_path = try std.mem.concat(maivi.allocator, u8, &.{ modname, ".lua" }); + defer maivi.allocator.free(new_path); + break :blk try helpers.read_file(chunk_allocator, dir, new_path); + }, + else => return err, + }; + errdefer chunk_allocator.free(contents); + + fern.debug().ctx("lua.loadfile") + .msg("saving contents") + .str("modname", modname).log(); + const modnameZ = try maivi.allocator.dupeZ(u8, modname); + defer maivi.allocator.free(modnameZ); + + if (packages.contains(modname)) return error.Clobber; + try packages.put(modname, contents); +} + +pub fn lua_require(l: *Lua) !i32 { + const modname = try l.toString(-1); + l.pop(1); + fern.debug().ctx("lua.require").str("modname", modname).log(); + + loadchunk(l, modname) catch |err| switch (err) { + error.PackageNotFound => { + try loadfile(modname); + try loadchunk(l, modname); + }, + else => return err, + }; + + return 1; +} diff --git a/src/lua/pcall.zig b/src/lua/pcall.zig new file mode 100644 index 0000000..9baf082 --- /dev/null +++ b/src/lua/pcall.zig @@ -0,0 +1,29 @@ +const std = @import("std"); + +const fern = @import("fern"); +const zlua = @import("zlua"); +pub const Lua = zlua.Lua; + +const maivi = @import("../root.zig"); + +/// * Pops from Stack: `(args.args + 1)` +/// * Pushes to Stack: `(args.results|1)` +pub fn pcall(l: *Lua, args: Lua.ProtectedCallArgs) !void { + return l.protectedCall(args) catch |err| switch (err) { + error.LuaRuntime => { + const msg = try l.toString(-1); + l.pop(1); + l.traceback(l, msg, 1); + const str = try l.toString(-1); + l.pop(1); + const e = try maivi.allocator.dupe(u8, str); + defer maivi.allocator.free(e); + fern.err().ctx("lua.pcall").str("err", e).log(); + return err; + }, + else => { + fern.err().ctx("lua.pcall").err(err).log(); + return err; + }, + }; +} diff --git a/src/lua/root.zig b/src/lua/root.zig new file mode 100644 index 0000000..2ecc215 --- /dev/null +++ b/src/lua/root.zig @@ -0,0 +1,120 @@ +const std = @import("std"); + +const fern = @import("fern"); +const opts = @import("opts"); +const zlua = @import("zlua"); +pub const Lua = zlua.Lua; + +const embed = @import("embed.zig"); +const lua_display = @import("lua_display.zig"); +const lua_print = @import("lua_print.zig").lua_print; +const lua_read = @import("lua_read.zig").lua_read; +const maivi = @import("../root.zig"); +const helpers = maivi.helpers; +pub const pcall = @import("pcall.zig").pcall; + +pub var lua: *Lua = undefined; + +pub fn init() !void { + fern.trace().ctx("lua.init").log(); + + lua = try lua_init(); +} + +pub fn lua_init() !*Lua { + const l = try Lua.init(maivi.allocator); + errdefer l.deinit(); + l.openBase(); + l.openOS(); + l.openDebug(); + l.openString(); + l.openTable(); + l.openMath(); + l.openBit32(); + + // package ================================================================ + + l.openPackage(); + + _ = l.getGlobal("package"); + + const path = try std.fmt.allocPrint(maivi.allocator, "{s}/?.lua;{s}/?/init.lua", .{ maivi.config.luaroot, maivi.config.luaroot }); + defer maivi.allocator.free(path); + _ = l.pushString(path); + l.setField(-2, "path"); + + l.pop(1); // pops "package" + + // global fn ============================================================== + + l.register("print", zlua.wrap(lua_print)); + l.register("read", zlua.wrap(lua_read)); + + // maivi ====================================================================== + + try l.loadBuffer(embed.maivi_module, "@runtime/maivi.lua"); + try pcall(l, .{ .results = 1 }); + l.setGlobal("maivi"); + + // h ========================================================================== + + try l.loadBuffer(embed.h_module, "@runtime/h.lua"); + try pcall(l, .{ .results = 1 }); + l.setGlobal("h"); + + // this ======================================================================= + + try l.loadBuffer(embed.this_module, "@runtime/this.lua"); + try pcall(l, .{ .results = 1 }); + + _ = l.pushString(maivi.config.base_url); + l.setField(-2, "base_url"); + _ = l.pushString(opts.version); + l.setField(-2, "rev"); + + l.setGlobal("this"); + + // iter ======================================================================= + + try l.loadBuffer(embed.iter_module, "@runtime/iter.lua"); + try pcall(l, .{ .results = 1 }); + l.setGlobal("Iter"); + + // display ==================================================================== + + _ = l.getGlobal("maivi"); + + _ = try lua_display.lua_opendisplay(l); + l.setField(-2, "display"); + + _ = try maivi.Router.lua_openrouter(l); + l.setField(-2, "router"); + + l.pop(1); // pops "maivi" + + if (l.getTop() != 0) return error.UnexpectedStackSize; + + return l; +} + +pub fn deinit() void { + lua.deinit(); +} + +/// * Pops from Stack: `0` +/// * Pushes to Stack: `1` +fn do_require(l: *Lua, modname: []const u8) !void { + _ = l.getGlobal("require"); + _ = l.pushString(modname); + try pcall(l, .{ .args = 1, .results = 1 }); +} + +pub fn setup() !void { + // explicitly import `init.lua` + try do_require(lua, "init"); + if (lua.isNil(-1) or !lua.isFunction(-1)) { + fern.err().msg("lua/init.lua does not return an init function").log(); + return error.Unexpected; + } + try pcall(lua, .{ .args = 0, .results = 0 }); +} diff --git a/src/runtime/_meta.lua b/src/lua/runtime/_meta.lua similarity index 89% rename from src/runtime/_meta.lua rename to src/lua/runtime/_meta.lua index c789382..0468eb7 100644 --- a/src/runtime/_meta.lua +++ b/src/lua/runtime/_meta.lua @@ -23,6 +23,10 @@ _G.maivi.router = ... _G.maivi.display = ... +---@param str string +---@return string +function _G.maivi.escape(str) end + ---@param v any ---@return string function _G.maivi.display.elem(v) end diff --git a/src/runtime/h.lua b/src/lua/runtime/h.lua similarity index 100% rename from src/runtime/h.lua rename to src/lua/runtime/h.lua diff --git a/src/runtime/iter.lua b/src/lua/runtime/iter.lua similarity index 100% rename from src/runtime/iter.lua rename to src/lua/runtime/iter.lua diff --git a/src/runtime/maivi.lua b/src/lua/runtime/maivi.lua similarity index 100% rename from src/runtime/maivi.lua rename to src/lua/runtime/maivi.lua diff --git a/src/runtime/this.lua b/src/lua/runtime/this.lua similarity index 100% rename from src/runtime/this.lua rename to src/lua/runtime/this.lua diff --git a/src/root.zig b/src/root.zig index 95383c4..c8df34c 100644 --- a/src/root.zig +++ b/src/root.zig @@ -1,25 +1,18 @@ const std = @import("std"); - const Io = std.Io; const Allocator = std.mem.Allocator; -pub const Config = @import("config.zig"); - -pub const Router = @import("router.zig"); +const fern = @import("fern"); +const httpz = @import("httpz"); +pub const Config = @import("config.zig"); pub const data = @import("data.zig"); - +pub const gen = @import("gen.zig"); pub const Handler = @import("handler.zig"); pub const helpers = @import("helpers.zig"); - -pub const lua = @import("lua.zig"); - +pub const lua = @import("lua/root.zig"); +pub const Router = @import("router.zig"); pub const tar = @import("tar.zig"); -pub const gen = @import("gen.zig"); - -const fern = @import("fern"); - -const httpz = @import("httpz"); pub var build: bool = false; pub var dev: bool = false; @@ -67,7 +60,7 @@ pub fn init(init_io: Io, init_allocator: Allocator, cfg: *const Config) !void { try lua.init(); errdefer lua.deinit(); - try data.load_data(); + try data.load(); } } @@ -147,7 +140,7 @@ pub fn handle(h: *Handler, req: *helpers.Request, res: *helpers.Response) !void const user_agent = req.request.header("user-agent"); if (user_agent) |ua| if (std.mem.startsWith(u8, ua, "curl")) { - return try handlemd(h, req, res); + return try handletxt(h, req, res); }; var it = std.mem.splitBackwardsScalar(u8, req.path, '/'); @@ -196,17 +189,23 @@ fn handlebuild(_: *Handler, req: *helpers.Request, res: *helpers.Response, trans return try res.set_file(dir, new_path); } -pub fn handlemd(h: *Handler, req: *helpers.Request, res: *helpers.Response) !void { +pub fn handletxt(h: *Handler, req: *helpers.Request, res: *helpers.Response) !void { if (req.path.len == 1) { - fern.debug().ctx("maivi.handle") - .msg("using index.txt") + fern.debug().ctx("maivi.handletxt") .int("id", req.id) + .msg("redirecting to message path") + .str("path", MESSAGE_PATH) .log(); return res.redirect(MESSAGE_PATH, null); } const mdpath = try std.mem.concat(allocator, u8, &.{ "data", req.path, ".md" }); defer allocator.free(mdpath); + fern.debug().ctx("maivi.handletxt") + .int("id", req.id) + .str("path", mdpath) + .log(); + const dir = try std.Io.Dir.openDirAbsolute(io, config.root, .{}); return res.set_file(dir, mdpath) catch |err| switch (err) { error.FileNotFound => { diff --git a/src/router.zig b/src/router.zig index d6d97e6..d85128b 100644 --- a/src/router.zig +++ b/src/router.zig @@ -10,13 +10,13 @@ const httpz = @import("httpz"); const Self = @This(); -const Data = struct { +const Path = struct { f: i32, props: i32, }; router: ?*maivi.ServerRouter, -paths: std.hash_map.StringHashMap(Data), +paths: std.hash_map.StringHashMap(Path), pub fn init() Self { return .{ @@ -39,7 +39,18 @@ pub fn add(self: *Self, path: []const u8, f: maivi.Handler.Action, cfg: RouteCon try self.router.?.tryGet(path, f, cfg); } -pub fn lua_add(l: *Lua) !i32 { +// lua ======================================================================== + +pub fn lua_openrouter(l: *Lua) !i32 { + l.createTable(0, 1); + + l.pushFunction(zlua.wrap(lua_add)); + l.setField(-2, "add"); + + return 1; +} + +fn lua_add(l: *Lua) !i32 { fern.trace().ctx("router").msg("lua_add").log(); // stack: -1 => props -2 => function -3 => string -4 => router (empty table) @@ -87,7 +98,7 @@ pub fn action(h: *maivi.Handler, req: *maivi.helpers.Request, res: *maivi.helper const user_agent = req.request.header("user-agent"); if (user_agent) |ua| if (std.mem.startsWith(u8, ua, "curl")) { - return try maivi.handlemd(h, req, res); + return try maivi.handletxt(h, req, res); }; handle(h, req, res) catch |err| { diff --git a/test.lua b/test.lua index eb9100b..4dd5f7e 100644 --- a/test.lua +++ b/test.lua @@ -1 +1 @@ -maivi.mod('init') +print(maivi.escape("<3")) -- 2.51.2