Something went wrong. Try again.
🦦 a tiny cute lua web framework
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232const std = @import("std");
const zlua = @import("zlua");const Lua = zlua.Lua;const fern = @import("fern");
const display = @import("lua/lua_display.zig");const str = @import("lua/lua_str.zig");const maivi = @import("root.zig");
// void elements// https://developer.mozilla.org/en-US/docs/Glossary/Void_elementconst VOID_ELEMENTS = [_][]const u8{ "area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "source", "wbr",};
const ElementConfig = struct { close: bool = true, empty: bool = false, raw: bool = false,
pub fn default(ntype: ?[]const u8) @This() { const name = ntype orelse return .{};
for (VOID_ELEMENTS) |n| if (std.mem.eql(u8, name, n)) return .{ .close = false, };
return .{}; }};
pub const Error = error{ ExpectedTable, ExpectedString,} || error{ LuaTableMissingValue, LuaValueNotATable, LuaRuntime, LuaMsgHandler, UnexpectedStackSize,} || std.Io.Writer.Error || std.mem.Allocator.Error;fn elem(l: *Lua, w: *std.Io.Writer) Error!void { const stack_size_on_entry = l.getTop();
if (!l.isTable(-1)) return error.ExpectedTable;
const name: ?[]u8 = blk: { const ltype_type = l.getField(-1, "type"); defer l.pop(1); if (ltype_type == .nil) break :blk null; if (ltype_type != .string) return error.ExpectedString;
l.pushValue(-1); defer l.pop(1); const namez = try l.toString(-1); break :blk try maivi.allocator.dupe(u8, namez); }; defer if (name) |m| maivi.allocator.free(m);
const config: ElementConfig = blk: { l.getMetatable(-1) catch break :blk null; defer l.pop(1);
const ltype_config = l.getField(-1, "config"); defer l.pop(1); if (ltype_config == .nil) break :blk null; if (ltype_config != .table) return error.ExpectedTable; break :blk try l.toStruct(ElementConfig, null, false, -1); } orelse .default(name);
if (name) |n| { try w.writeByte('<'); try w.writeAll(n); }
// props props: { const ltype_props = l.getField(-1, "props"); defer l.pop(1); if (ltype_props == .nil) break :props; if (ltype_props != .table) return error.ExpectedTable;
try writeProps(l, w); }
{ const nchildren: usize = nchildren: { const ltype_children = l.getField(-1, "children"); if (ltype_children == .nil) break :nchildren 0; if (ltype_children != .table) return error.ExpectedTable; break :nchildren l.lenRaw(-1); }; // -1 => children; -2 => elem defer l.pop(1); // pops "children"
if (config.empty and nchildren == 0) { if (name != null) try w.writeAll(" />");
try w.flush(); return; }
if (name != null) try w.writeByte('>');
// children if (nchildren > 0) try writeChildren(l, w, config); }
// close if (config.close) if (name) |n| { try w.writeAll("</"); try w.writeAll(n); try w.writeByte('>'); };
if (l.getTop() != stack_size_on_entry) return error.UnexpectedStackSize;
try w.flush();}
fn writeProps(l: *Lua, w: *std.Io.Writer) !void { var arena: std.heap.ArenaAllocator = .init(maivi.allocator); defer arena.deinit(); var allocator = arena.allocator();
l.pushValue(-1); defer l.pop(1);
l.pushNil(); // stack now contains: -1 => nil; -2 => table
while (l.next(-2)) { // stack now contains: -1 => value; -2 => key; -3 => table
try w.writeByte(' ');
const k = k: { l.pushValue(-2); defer l.pop(1);
const kz = try l.toString(-1); break :k try allocator.dupe(u8, kz); };
// uses -1 => value _ = try display.attr(l); // stack now contains: -1 => value (parsed); -2 => value; -3 => key; -4 => table
// uses -1 => value (parsed) _ = try str.escape(l); // stack now contains: -1 => value (escaped); -2 => value (parsed); -3 => value; -4 => key; -5 => table
const vz = try l.toString(-1); const v = try allocator.dupe(u8, vz);
try w.writeAll(k); try w.writeByte('='); try w.writeByte('"'); try w.writeAll(v); try w.writeByte('"');
// pop value + parsed + escaped, leaving original key l.pop(3); // stack now contains: -1 => key; -2 => table }}
fn writeChildren(l: *Lua, w: *std.Io.Writer, config: ElementConfig) !void { var arena: std.heap.ArenaAllocator = .init(maivi.allocator); defer arena.deinit(); var allocator = arena.allocator();
l.pushValue(-1); defer l.pop(1);
l.pushNil(); // stack now contains: -1 => nil; -2 => table
while (l.next(-2)) { // stack now contains: -1 => value; -2 => key; -3 => table
if (l.isFunction(-1)) { try maivi.lua.pcall(l, .{ .results = 1 }); }
if (!l.isTable(-1)) { // uses -1 => value _ = try display.elem(l); // stack now contains: -1 => value (parsed); -2 => value; -3 => key; -4 => table
if (!config.raw) { // uses -1 => value (parsed) _ = try str.escape(l); // stack now contains: -1 => value (escaped); -2 => value (parsed); -3 => value; -4 => key; -5 => table }
const vz = try l.toString(-1); const v = try allocator.dupe(u8, vz);
if (!config.raw) l.pop(1); // pop escaped l.pop(1); // pop parsed
try w.writeAll(v); } else { // uses -1 => value try elem(l, w); }
// pop value, leaving original key l.pop(1); // stack now contains: -1 => key; -2 => table }}
pub fn render(l: *Lua, w: *std.Io.Writer) !void { try elem(l, w);}