size: 2 KiB

1const std = @import("std");
2const Io = std.Io;
3const t = std.testing;
4const mem = std.mem;
5const assert = std.debug.assert;
6const zlua = @import("zlua");
7const djot_lua = @embedFile("vendor/djot.lua/djot.lua");
8
9const Djot = @This();
10
11lua: *zlua.Lua,
12
13/// Must call deinit() when done.
14pub fn init(gpa: mem.Allocator) !Djot {
15 var lua = try zlua.Lua.init(gpa);
16 lua.openLibs();
17
18 const djot = Djot{ .lua = lua };
19
20 lua.doString(djot_lua) catch |err| {
21 try djot.printError();
22 return err;
23 };
24
25 lua.doString(@embedFile("Djot.lua")) catch |err| {
26 try djot.printError();
27 return err;
28 };
29
30 return djot;
31}
32
33/// Clean up after the lua VM
34pub fn deinit(self: *const Djot) void {
35 self.lua.deinit();
36}
37
38/// Caller owns returned memory
39pub fn toHtml(self: *const Djot, gpa: mem.Allocator, djot_text: []const u8) ![]const u8 {
40 // call the global function
41 assert(try self.lua.getGlobal("djot_to_html") == .function);
42 _ = self.lua.pushString(djot_text);
43 self.lua.protectedCall(.{ .args = 1, .results = 1 }) catch |err| {
44 try self.printError();
45 return err;
46 };
47
48 const output = self.lua.toString(-1) catch |err| {
49 try self.printError();
50 return err;
51 };
52 const result = gpa.dupe(u8, output);
53
54 // All done. Pop previous result from stack.
55 self.lua.pop(1);
56
57 return result;
58}
59
60pub fn writeHtml(self: *const Djot, writer: *Io.Writer, djot_text: []const u8) !void {
61 // call the global function
62 assert(try self.lua.getGlobal("djot_to_html") == .function);
63 _ = self.lua.pushString(djot_text);
64 self.lua.protectedCall(.{ .args = 1, .results = 1 }) catch |err| {
65 try self.printError();
66 return err;
67 };
68
69 const output = self.lua.toString(-1) catch |err| {
70 try self.printError();
71 return err;
72 };
73
74 try writer.writeAll(output);
75
76 // All done. Pop previous result from stack.
77 self.lua.pop(1);
78}
79
80fn printError(self: *const Djot) !void {
81 std.debug.print("lua error: {s}\n", .{try self.lua.toString(-1)});
82}
83
84test toHtml {
85 const gpa = std.testing.allocator;
86
87 const djot = try Djot.init(gpa);
88 defer djot.deinit();
89
90 const cases = .{
91 .{ "*bold*", "<p><strong>bold</strong></p>\n" },
92 .{
93 "# foo",
94 \\<section id="foo">
95 \\<h1>foo</h1>
96 \\</section>
97 \\
98 },
99 };
100
101 var writerBuf: [4096]u8 = undefined;
102
103 inline for (cases) |case| {
104 const input = case[0];
105 const expected = case[1];
106
107 // test toHtml
108 const actual = try djot.toHtml(gpa, input);
109 defer gpa.free(actual);
110 try t.expectEqualStrings(expected, actual);
111
112 // test writeHtml
113 var writer = Io.Writer.fixed(&writerBuf);
114 try djot.writeHtml(&writer, input);
115 try t.expectEqualStrings(expected, writer.buffered());
116 try writer.flush();
117 }
118}