update: lorem

This commit is contained in:
Lucas F. 2026-01-18 16:00:11 -03:00
parent 14e307f3dc
commit 24ecea0244
4 changed files with 853 additions and 201 deletions

338
src/lorem.zig Normal file
View file

@ -0,0 +1,338 @@
const std = @import("std");
const rand = std.crypto.random;
pub const LOREM_COMMON_P =
\\Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
\\tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
\\veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
\\commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
\\velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
\\occaecat cupidatat non proident, sunt in culpa qui officia deserunt
\\mollit anim id est laborum.
;
pub const LOREM_WORDS = [182][]const u8{
"exercitationem",
"perferendis",
"perspiciatis",
"laborum",
"eveniet",
"sunt",
"iure",
"nam",
"nobis",
"eum",
"cum",
"officiis",
"excepturi",
"odio",
"consectetur",
"quasi",
"aut",
"quisquam",
"vel",
"eligendi",
"itaque",
"non",
"odit",
"tempore",
"quaerat",
"dignissimos",
"facilis",
"neque",
"nihil",
"expedita",
"vitae",
"vero",
"ipsum",
"nisi",
"animi",
"cumque",
"pariatur",
"velit",
"modi",
"natus",
"iusto",
"eaque",
"sequi",
"illo",
"sed",
"ex",
"et",
"voluptatibus",
"tempora",
"veritatis",
"ratione",
"assumenda",
"incidunt",
"nostrum",
"placeat",
"aliquid",
"fuga",
"provident",
"praesentium",
"rem",
"necessitatibus",
"suscipit",
"adipisci",
"quidem",
"possimus",
"voluptas",
"debitis",
"sint",
"accusantium",
"unde",
"sapiente",
"voluptate",
"qui",
"aspernatur",
"laudantium",
"soluta",
"amet",
"quo",
"aliquam",
"saepe",
"culpa",
"libero",
"ipsa",
"dicta",
"reiciendis",
"nesciunt",
"doloribus",
"autem",
"impedit",
"minima",
"maiores",
"repudiandae",
"ipsam",
"obcaecati",
"ullam",
"enim",
"totam",
"delectus",
"ducimus",
"quis",
"voluptates",
"dolores",
"molestiae",
"harum",
"dolorem",
"quia",
"voluptatem",
"molestias",
"magni",
"distinctio",
"omnis",
"illum",
"dolorum",
"voluptatum",
"ea",
"quas",
"quam",
"corporis",
"quae",
"blanditiis",
"atque",
"deserunt",
"laboriosam",
"earum",
"consequuntur",
"hic",
"cupiditate",
"quibusdam",
"accusamus",
"ut",
"rerum",
"error",
"minus",
"eius",
"ab",
"ad",
"nemo",
"fugit",
"officia",
"at",
"in",
"id",
"quos",
"reprehenderit",
"numquam",
"iste",
"fugiat",
"sit",
"inventore",
"beatae",
"repellendus",
"magnam",
"recusandae",
"quod",
"explicabo",
"doloremque",
"aperiam",
"consequatur",
"asperiores",
"commodi",
"optio",
"dolor",
"labore",
"temporibus",
"repellat",
"veniam",
"architecto",
"est",
"esse",
"mollitia",
"nulla",
"a",
"similique",
"eos",
"alias",
"dolore",
"tenetur",
"deleniti",
"porro",
"facere",
"maxime",
"corrupti",
};
pub const LOREM_COMMON_WORDS = [19][]const u8{
"lorem",
"ipsum",
"dolor",
"sit",
"amet",
"consectetur",
"adipisicing",
"elit",
"sed",
"do",
"eiusmod",
"tempor",
"incididunt",
"ut",
"labore",
"et",
"dolore",
"magna",
"aliqua",
};
pub fn sentence(allocator: std.mem.Allocator) ![]const u8 {
const num_sections = rand.intRangeAtMost(u32, 1, 4);
var parts = std.ArrayList([]u8){};
defer {
for (parts.items) |p| allocator.free(p);
parts.deinit(allocator);
}
var i: u32 = 0;
while (i < num_sections) : (i += 1) {
const num_words = rand.intRangeAtMost(u32, 3, 12);
var wds = std.ArrayList([]const u8){};
defer wds.deinit(allocator);
try wds.ensureTotalCapacity(allocator, num_words);
var j: u32 = 0;
while (j < num_words) : (j += 1) {
const idx = rand.intRangeAtMost(usize, 0, LOREM_WORDS.len - 1);
try wds.append(allocator, LOREM_WORDS[idx]);
}
const section = try std.mem.join(allocator, " ", wds.items);
try parts.append(allocator, section);
}
const text = try std.mem.join(allocator, ", ", parts.items);
defer allocator.free(text);
var result = try allocator.alloc(u8, text.len + 1);
if (text.len > 0) {
result[0] = std.ascii.toUpper(text[0]);
@memcpy(result[1..text.len], text[1..]);
}
result[text.len] = if (rand.boolean()) '.' else '?';
return result;
}
pub fn paragraph(allocator: std.mem.Allocator) ![]const u8 {
const num_sentences = rand.intRangeAtMost(u32, 1, 4);
var sentences = std.ArrayList([]const u8){};
defer sentences.deinit(allocator);
for (0..num_sentences) |_| {
try sentences.append(allocator, try sentence(allocator));
}
return try std.mem.join(allocator, ". ", sentences.items);
}
pub fn paragraphs(allocator: std.mem.Allocator, count: u32, random: bool) ![]const u8 {
var pa = std.ArrayList([]const u8){};
defer pa.deinit(allocator);
if (count == 0) return "";
if (random == true) {
for (0..count) |_| {
const pg = try paragraph(allocator);
if (pg.len > 0) {
try pa.append(allocator, try std.fmt.allocPrint(allocator, "<p>{s}</p>", .{pg}));
}
}
return try std.mem.join(allocator, "\n", pa.items);
}
const first = try std.fmt.allocPrint(allocator, "<p>{s}</p>", .{LOREM_COMMON_P});
if (count == 1) {
return first;
}
const ncount: u32 = count - 1;
try pa.append(allocator, first);
for (0..ncount) |_| {
const pg = try paragraph(allocator);
if (pg.len > 0) {
try pa.append(allocator, try std.fmt.allocPrint(allocator, "<p>{s}</p>", .{pg}));
}
}
return try std.mem.join(allocator, "\n", pa.items);
}
pub fn words(allocator: std.mem.Allocator, count: u32, random: bool) ![]const u8 {
var wd = std.ArrayList([]const u8){};
defer wd.deinit(allocator);
if (random == true) {
for (0..count) |_| {
const idx = rand.intRangeAtMost(usize, 0, LOREM_COMMON_WORDS.len - 1);
try wd.append(allocator, LOREM_COMMON_WORDS[idx]);
}
return try std.mem.join(allocator, " ", wd.items);
}
var inc: u32 = 0;
for (LOREM_COMMON_WORDS) |word| {
try wd.append(allocator, word);
inc += 1;
if (inc >= count or inc >= 20) break;
}
if (count >= 20) {
const ncount = count - inc;
for (0..ncount) |_| {
const idx = rand.intRangeAtMost(usize, 0, LOREM_COMMON_WORDS.len - 1);
try wd.append(allocator, LOREM_COMMON_WORDS[idx]);
}
}
return try std.mem.join(allocator, " ", wd.items);
}

View file

@ -202,6 +202,7 @@ pub const FilterBlockNode = struct {
pub const FirstOfNode = struct {
values: []const []const u8,
fallback: []const u8,
};
pub const ForNode = struct {
@ -232,7 +233,7 @@ pub const LoadNode = struct {
pub const LoremNode = struct {
count: ?[]const u8 = null, // "3" ou null
method: ?[]const u8 = null, // "p" ou "w" ou null
format: ?[]const u8 = null, // "html" ou null
random: bool = false,
};
pub const NowNode = struct {
@ -450,10 +451,12 @@ pub const Node = struct {
values_copy[i] = try allocator.dupe(u8, f);
}
const fallback_copy = try allocator.dupe(u8, self.tag.?.body.firstof.fallback);
return Node{ .type = .tag, .tag = .{
.kind = .firstof,
.args = try allocator.dupe(u8, self.tag.?.args),
.body = .{ .firstof = .{ .values = values_copy } },
.body = .{ .firstof = .{ .values = values_copy, .fallback = fallback_copy } },
.raw = try allocator.dupe(u8, self.tag.?.raw),
} };
},
@ -776,7 +779,7 @@ pub const Node = struct {
.lorem = .{
.count = if (self.tag.?.body.lorem.count) |c| try allocator.dupe(u8, c) else null,
.method = if (self.tag.?.body.lorem.method) |m| try allocator.dupe(u8, m) else null,
.format = if (self.tag.?.body.lorem.format) |f| try allocator.dupe(u8, f) else null,
.random = self.tag.?.body.lorem.random,
},
},
},
@ -1843,6 +1846,7 @@ pub const Parser = struct {
var values = std.ArrayList([]const u8){};
defer values.deinit(allocator);
var i: usize = 0;
var fallback: []const u8 = "";
while (i < args.len) {
while (i < args.len and std.ascii.isWhitespace(args[i])) : (i += 1) {}
@ -1870,12 +1874,17 @@ pub const Parser = struct {
}
const value = std.mem.trim(u8, args[start..i], " \t\r\n\"'");
if (in_quote) {
fallback = value;
} else {
try values.append(allocator, value);
}
}
return TagNodeBody{
.firstof = .{
.values = try values.toOwnedSlice(allocator),
.fallback = fallback,
},
};
},
@ -1936,7 +1945,7 @@ pub const Parser = struct {
const args = tag_node.tag.?.args;
var count: ?[]const u8 = null;
var method: ?[]const u8 = null;
var format: ?[]const u8 = null;
var random: bool = false;
var parts = std.mem.splitScalar(u8, args, ' ');
while (parts.next()) |part| {
@ -1950,8 +1959,8 @@ pub const Parser = struct {
if (std.mem.eql(u8, trimmed, "p") or std.mem.eql(u8, trimmed, "w")) {
method = trimmed;
} else if (std.mem.eql(u8, trimmed, "html") or std.mem.eql(u8, trimmed, "text")) {
format = trimmed;
} else if (std.mem.eql(u8, trimmed, "true")) {
random = true;
}
}
@ -1959,7 +1968,7 @@ pub const Parser = struct {
.lorem = .{
.count = count,
.method = method,
.format = format,
.random = random,
},
};
},

View file

@ -11,6 +11,7 @@ const FilterError = @import("filters.zig").FilterError;
const parser = @import("parser.zig");
const TemplateCache = @import("cache.zig").TemplateCache;
const time = @import("time.zig");
const lorem = @import("lorem.zig");
pub const RenderError = error{
InvalidCharacter,
@ -315,175 +316,85 @@ pub const Renderer = struct {
const datetime = try time.Time.now().toStringAlloc(alloc, format);
try writer.writeAll(datetime);
},
else => {},
}
.csrf_token => {
const token = self.context.get("csrf_token");
if (token == null) return;
try writer.writeAll(std.fmt.allocPrint(alloc, "<input type=\"hidden\" name=\"csrfmiddlewaretoken\" value=\"{s}\">", .{token.?.string}) catch "");
},
}
}
fn renderNode_old(self: *const Renderer, alloc: Allocator, nodes: []parser.Node, node: parser.Node, writer: anytype, context: ?*Context, parent_block_nodes: ?[]parser.Node) RenderError!void {
switch (node.type) {
.text => try writer.writeAll(node.text.?.content),
.variable => {
const var_name = node.variable.?.expr;
var value: Value = Value.null;
if (context != null) {
value = context.?.get(var_name) orelse Value.null;
} else {
value = self.context.get(var_name) orelse Value.null;
}
var is_safe = false;
for (node.variable.?.filters) |f| {
const filter_fn = builtin_filters.get(f.name) orelse return error.UnknownFilter;
// const arg = if (f.arg) |a| Value{ .string = a } else null;
var arg: Value = Value.null;
if (f.arg) |a| {
arg = Value{ .string = a };
const result = try std.fmt.parseInt(i64, a, 10);
if (std.math.maxInt(i64) < result) return error.Overflow;
arg = Value{ .int = result };
}
value = try filter_fn(alloc, value, arg);
if (std.mem.eql(u8, f.name, "safe")) is_safe = true;
}
if (!is_safe) {
if (builtin_filters.get("escape")) |escape_fn| {
value = try escape_fn(alloc, value, null);
}
}
.firstof => {
const values = node.tag.?.body.firstof.values;
for (values) |value| {
if (self.context.get(value)) |v| {
if (!isTruthy(v)) continue;
var buf = ArrayListUnmanaged(u8){};
defer buf.deinit(alloc);
try self.valueToString(alloc, &buf, value);
try self.valueToString(alloc, &buf, v);
try writer.writeAll(buf.items);
},
.if_block => {
const condition = try self.evaluateCondition(alloc, node.@"if".?.condition);
if (condition) {
for (node.@"if".?.true_body) |child| {
try self.renderNode(alloc, nodes, child, writer, null, null);
}
return;
} else {
for (node.@"if".?.false_body) |child| {
try self.renderNode(alloc, nodes, child, writer, null, null);
const check_value = self.resolveStringVariable(value).?;
if (!isTruthy(check_value)) continue;
var buf = ArrayListUnmanaged(u8){};
defer buf.deinit(alloc);
try self.valueToString(alloc, &buf, Value{ .string = value });
try writer.writeAll(buf.items);
return;
}
}
try writer.writeAll(node.tag.?.body.firstof.fallback);
},
.include => {
const included_template = try self.readTemplateFile(node.include.?.template_name);
defer alloc.free(included_template);
.lorem => {
const count = node.tag.?.body.lorem.count;
const method = node.tag.?.body.lorem.method;
const random = node.tag.?.body.lorem.random;
var included_parser = parser.Parser.init(included_template);
const included_nodes = try included_parser.parse(alloc);
defer {
for (included_nodes) |n| n.deinit(alloc);
alloc.free(included_nodes);
if (count == null and method == null) {
if (random == false) {
try writer.writeAll(lorem.LOREM_COMMON_P);
return;
}else {
try writer.writeAll(try lorem.sentence(alloc));
return;
}
}
// Renderiza o include no contexto atual (sem novo contexto)
for (included_nodes) |included_node| {
try self.renderNode(alloc, nodes, included_node, writer, context, null);
const ncount: u32 = std.fmt.parseInt(u32, count.?, 10) catch 1;
if (std.mem.eql(u8, method.?, "p")) {
const lorem_ = try lorem.paragraphs(alloc, ncount, random);
try writer.writeAll(lorem_);
return;
} else {
const lorem_ = try lorem.words(alloc, ncount, random);
try writer.writeAll(lorem_);
return;
}
},
.for_block => {
const list_value = self.context.get(node.@"for".?.iterable) orelse Value.null;
const list = switch (list_value) {
.list => |l| l,
else => return,
};
for (list) |item| {
var ctx = Context.init(alloc);
defer ctx.deinit();
try ctx.set(node.@"for".?.loop_var, item);
for (node.@"for".?.body) |child| {
try self.renderNode(alloc, nodes, child, writer, &ctx, null);
}
if (node.@"for".?.body.len == 0) {
for (node.@"for".?.empty_body) |child| {
try self.renderNode(alloc, nodes, child, writer, &ctx, null);
}
}
}
},
.super => {
if (parent_block_nodes) |parent| {
for (parent) |child| {
try self.renderNode(alloc, nodes, child, writer, null, null);
}
}
},
.block => {
for (node.block.?.body) |child| {
const parent_content = parent_block_nodes orelse node.block.?.body;
try self.renderNode(alloc, nodes, child, writer, null, parent_content);
}
},
.widthratio => {
var divisor: Value = Value{ .float = 1.0 };
var float_divisor: f64 = 1.0;
var value: Value = Value{ .float = 1.0 };
var float_value: f64 = 1.0;
var max_value: Value = Value{ .float = 1.0 };
var float_max_value: f64 = 1.0;
if (!std.mem.eql(u8, node.widthratio.?.value, "")) {
value = Value{ .string = node.widthratio.?.value };
if (self.context.get(node.widthratio.?.value)) |v| {
value = v;
}
float_value = switch (value) {
.int => @as(f64, @floatFromInt(value.int)),
.float => value.float,
.string => std.fmt.parseFloat(f64, value.string) catch 1.0,
else => 1.0,
};
}
if (!std.mem.eql(u8, node.widthratio.?.max_value, "")) {
max_value = Value{ .string = node.widthratio.?.max_value };
if (self.context.get(node.widthratio.?.max_value)) |v| {
max_value = v;
}
float_max_value = switch (max_value) {
.int => @as(f64, @floatFromInt(max_value.int)),
.float => max_value.float,
.string => std.fmt.parseFloat(f64, max_value.string) catch 1.0,
else => 1.0,
};
}
if (node.widthratio.?.divisor) |div| {
divisor = Value{ .string = div };
if (self.context.get(div)) |d| {
divisor = d;
}
float_divisor = switch (divisor) {
.int => @as(f64, @floatFromInt(divisor.int)),
.float => divisor.float,
.string => std.fmt.parseFloat(f64, divisor.string) catch 0.0,
else => 1.0,
};
}
const ratio = (float_value / float_max_value) * float_divisor;
try writer.writeAll(std.fmt.allocPrint(alloc, "{d}", .{ratio}) catch "0");
},
else => {},
}
},
}
}
fn resolveStringVariable(self: *const Renderer, value: []const u8) ?Value {
_ = self;
if (std.mem.eql(u8, value, "true")) return Value{ .bool = true };
if (std.mem.eql(u8, value, "false")) return Value{ .bool = false };
const is_int = std.fmt.parseInt(i64, value, 10) catch |err| switch (err) {
error.InvalidCharacter => null,
error.Overflow => null,
};
if (is_int != null) return Value{ .int = is_int.? };
const is_float = std.fmt.parseFloat(f64, value) catch |err| switch (err) {
error.InvalidCharacter => null,
};
if (is_float != null) return Value{ .float = is_float.? };
return Value{ .string = value };
}
fn findChildBlock(self: *const Renderer, nodes: []parser.Node, name: []const u8) ?parser.BlockNode {
@ -496,35 +407,26 @@ pub const Renderer = struct {
return null;
}
fn toValue(self: *Context, value: anytype) RenderError!Value {
const T = @TypeOf(value);
return switch (@typeInfo(T)) {
.bool => Value{ .bool = value },
.int, .comptime_int => Value{ .int = @intCast(value) },
.float, .comptime_float => Value{ .float = @floatCast(value) },
.pointer => Value{ .string = try std.fmt.allocPrint(self.allocator(), "{s}", .{value}) },
.@"struct" => blk: {
var dict = std.StringHashMapUnmanaged(Value){};
inline for (std.meta.fields(T)) |field| {
const field_val = @field(value, field.name);
const converted = try self.toValue(field_val);
try dict.put(self.allocator(), field.name, converted);
}
break :blk Value{ .dict = dict };
},
.array => blk: {
var list = try self.allocator().alloc(Value, value.len);
for (value, 0..) |item, i| {
list[i] = try self.toValue(item);
}
break :blk Value{ .list = list };
},
.optional => if (value) |v| try self.toValue(v) else .null,
.null => .null,
// CASO ESPECIAL: o valor é um Value (ex: lista de Value)
.@"union" => if (T == Value) value else @compileError("Unsupported union type: " ++ @typeName(T)),
else => @compileError("Unsupported type: " ++ @typeName(T)),
fn escapeHtml(self: *const Renderer, value: Value) !Value {
const s = switch (value) {
.string => |str| str,
else => return value,
};
var result = std.ArrayList(u8){};
for (s) |c| {
switch (c) {
'&' => try result.appendSlice(self.allocator, "&amp;"),
'<' => try result.appendSlice(self.allocator, "&lt;"),
'>' => try result.appendSlice(self.allocator, "&gt;"),
'"' => try result.appendSlice(self.allocator, "&quot;"),
'\'' => try result.appendSlice(self.allocator, "&#x27;"),
else => try result.append(self.allocator, c),
}
}
return Value{ .string = try result.toOwnedSlice(self.allocator) };
}
fn valueToString(self: *const Renderer, alloc: Allocator, buf: *ArrayListUnmanaged(u8), value: Value) RenderError!void {

View file

@ -652,8 +652,25 @@ test "renderer - now" {
try ctx.set("idade", Value{ .int = 20 });
const template =
// \\{% now \"Y-m-d H:i:s\" %}
\\{% now %}
\\{% now \"Y-m-d H:i:s\" %}
\\{% now \"Y" %}
\\{% now \"m\" %}
\\{% now \"n\" %}
\\{% now \"d\" %}
\\{% now \"j\" %}
\\{% now \"F\" %}
\\{% now \"M\" %}
\\{% now \"l\" %}
\\{% now \"D\" %}
\\{% now \"H:i:s\" %}
\\{% now \"H\" %}
\\{% now \"G\" %}
\\{% now \"i\" %}
\\{% now \"s\" %}
\\{% now \"a\" %}
\\{% now \"A\" %}
\\{% now \"P\" %}
;
var buf = std.ArrayList(u8){};
@ -665,3 +682,389 @@ test "renderer - now" {
// try testing.expect(std.mem.indexOf(u8, buf.items, "Maior") != null);
}
test "renderer - csrf_token in context" {
std.debug.print("____________________________________________________\n", .{});
std.debug.print("15 - csrf_token in context\n", .{});
const alloc = testing.allocator;
var ctx = Context.init(alloc);
defer ctx.deinit();
var cache = TemplateCache.init(alloc);
defer cache.deinit();
const renderer = Renderer.init(&ctx, &cache);
const token: []const u8 = "zh5fyUSICjXNsDTtJCjl9A3O2dDSHhYFlIngAEO6PXK9NX56Z1XLEy7doYuPcE0u";
try ctx.set("csrf_token", token);
const template =
\\{% csrf_token %}
;
const expected =
\\<input type="hidden" name="csrfmiddlewaretoken" value="zh5fyUSICjXNsDTtJCjl9A3O2dDSHhYFlIngAEO6PXK9NX56Z1XLEy7doYuPcE0u">
;
var buf = std.ArrayList(u8){};
defer buf.deinit(alloc);
try renderer.renderString(template, buf.writer(alloc));
std.debug.print("OUTPUT:\n\n{s}\n", .{buf.items});
try testing.expectEqualStrings(expected, buf.items);
}
test "renderer - csrf_token not in context" {
std.debug.print("____________________________________________________\n", .{});
std.debug.print("16 - csrf_token not in context\n", .{});
const alloc = testing.allocator;
var ctx = Context.init(alloc);
defer ctx.deinit();
var cache = TemplateCache.init(alloc);
defer cache.deinit();
const renderer = Renderer.init(&ctx, &cache);
const template =
\\{% csrf_token %}
;
const expected =
\\
;
var buf = std.ArrayList(u8){};
defer buf.deinit(alloc);
try renderer.renderString(template, buf.writer(alloc));
// std.debug.print("OUTPUT:\n\n{s}\n", .{buf.items});
try testing.expectEqualStrings(expected, buf.items);
}
// TODO: add parse filters to variables
test "renderer - firstof withtout fallback" {
std.debug.print("____________________________________________________\n", .{});
std.debug.print("17 - firstof without fallback\n", .{});
const alloc = testing.allocator;
var ctx = Context.init(alloc);
defer ctx.deinit();
var cache = TemplateCache.init(alloc);
defer cache.deinit();
const renderer = Renderer.init(&ctx, &cache);
try ctx.set("var1", "");
try ctx.set("var2", "baz");
const template =
\\{% firstof var1 var2 %}
;
const expected =
\\baz
;
var buf = std.ArrayList(u8){};
defer buf.deinit(alloc);
try renderer.renderString(template, buf.writer(alloc));
// std.debug.print("OUTPUT:\n\n{s}\n", .{buf.items});
try testing.expectEqualStrings(expected, buf.items);
}
test "renderer - firstof with fallback" {
std.debug.print("____________________________________________________\n", .{});
std.debug.print("18 - firstof with fallback\n", .{});
const alloc = testing.allocator;
var ctx = Context.init(alloc);
defer ctx.deinit();
var cache = TemplateCache.init(alloc);
defer cache.deinit();
const renderer = Renderer.init(&ctx, &cache);
try ctx.set("var1", "");
try ctx.set("var2", 0);
const template =
\\{% firstof var1 var2 "Oops no value!" %}
;
const expected =
\\Oops no value!
;
var buf = std.ArrayList(u8){};
defer buf.deinit(alloc);
try renderer.renderString(template, buf.writer(alloc));
// std.debug.print("OUTPUT:\n\n{s}\n", .{buf.items});
try testing.expectEqualStrings(expected, buf.items);
}
test "renderer - firstof without value in context" {
std.debug.print("____________________________________________________\n", .{});
std.debug.print("19 - firstof without value in context\n", .{});
const alloc = testing.allocator;
var ctx = Context.init(alloc);
defer ctx.deinit();
var cache = TemplateCache.init(alloc);
defer cache.deinit();
const renderer = Renderer.init(&ctx, &cache);
const template =
\\{% firstof 0 true "Oops no value!" %}
;
const expected =
\\true
;
var buf = std.ArrayList(u8){};
defer buf.deinit(alloc);
try renderer.renderString(template, buf.writer(alloc));
// std.debug.print("OUTPUT:\n\n{s}\n", .{buf.items});
try testing.expectEqualStrings(expected, buf.items);
}
test "renderer - firstof missing vars" {
std.debug.print("____________________________________________________\n", .{});
std.debug.print("20 - firstof missing vars\n", .{});
const alloc = testing.allocator;
var ctx = Context.init(alloc);
defer ctx.deinit();
var cache = TemplateCache.init(alloc);
defer cache.deinit();
const renderer = Renderer.init(&ctx, &cache);
const template =
\\{% firstof %}
;
const expected =
\\
;
var buf = std.ArrayList(u8){};
defer buf.deinit(alloc);
try renderer.renderString(template, buf.writer(alloc));
// std.debug.print("OUTPUT:\n\n{s}\n", .{buf.items});
try testing.expectEqualStrings(expected, buf.items);
}
test "renderer - firstof missing vars with fallback" {
std.debug.print("____________________________________________________\n", .{});
std.debug.print("21 - firstof missing vars with fallback\n", .{});
const alloc = testing.allocator;
var ctx = Context.init(alloc);
defer ctx.deinit();
var cache = TemplateCache.init(alloc);
defer cache.deinit();
const renderer = Renderer.init(&ctx, &cache);
const template =
\\{% firstof "nothing here" %}
;
const expected =
\\nothing here
;
var buf = std.ArrayList(u8){};
defer buf.deinit(alloc);
try renderer.renderString(template, buf.writer(alloc));
// std.debug.print("OUTPUT:\n\n{s}\n", .{buf.items});
try testing.expectEqualStrings(expected, buf.items);
}
test "renderer - lorem" {
std.debug.print("____________________________________________________\n", .{});
std.debug.print("22 - lorem\n", .{});
const alloc = testing.allocator;
var ctx = Context.init(alloc);
defer ctx.deinit();
var cache = TemplateCache.init(alloc);
defer cache.deinit();
const renderer = Renderer.init(&ctx, &cache);
const template =
\\{% lorem %}
;
var buf = std.ArrayList(u8){};
defer buf.deinit(alloc);
try renderer.renderString(template, buf.writer(alloc));
std.debug.print("OUTPUT:\n\n{s}\n", .{buf.items});
try testing.expect(std.mem.indexOf(u8, buf.items, "Lorem ipsum") != null);
}
test "renderer - lorem with count and method words" {
std.debug.print("____________________________________________________\n", .{});
std.debug.print("23 - lorem with count and method words\n", .{});
const alloc = testing.allocator;
var ctx = Context.init(alloc);
defer ctx.deinit();
var cache = TemplateCache.init(alloc);
defer cache.deinit();
const renderer = Renderer.init(&ctx, &cache);
const template =
\\{% lorem 3 w %}
;
var buf = std.ArrayList(u8){};
defer buf.deinit(alloc);
try renderer.renderString(template, buf.writer(alloc));
std.debug.print("OUTPUT:\n\n{s}\n", .{buf.items});
try testing.expectEqualStrings("lorem ipsum dolor", buf.items);
}
test "renderer - lorem with count and method paragraphs" {
std.debug.print("____________________________________________________\n", .{});
std.debug.print("24 - lorem with count and method paragraphs\n", .{});
const alloc = testing.allocator;
var ctx = Context.init(alloc);
defer ctx.deinit();
var cache = TemplateCache.init(alloc);
defer cache.deinit();
const renderer = Renderer.init(&ctx, &cache);
const template =
\\{% lorem 5 p %}
;
var buf = std.ArrayList(u8){};
defer buf.deinit(alloc);
try renderer.renderString(template, buf.writer(alloc));
std.debug.print("OUTPUT:\n\n{s}\n", .{buf.items});
const qty = std.mem.count(u8, buf.items, "<p>");
try testing.expect(std.mem.indexOf(u8, buf.items, "Lorem ipsum dolor") != null);
try testing.expect(qty == 5);
}
test "renderer - lorem only random" {
std.debug.print("____________________________________________________\n", .{});
std.debug.print("25 - lorem only random\n", .{});
const alloc = testing.allocator;
var ctx = Context.init(alloc);
defer ctx.deinit();
var cache = TemplateCache.init(alloc);
defer cache.deinit();
const renderer = Renderer.init(&ctx, &cache);
const template =
\\{% lorem true %}
;
var buf = std.ArrayList(u8){};
defer buf.deinit(alloc);
try renderer.renderString(template, buf.writer(alloc));
std.debug.print("OUTPUT:\n\n{s}\n", .{buf.items});
try testing.expect(buf.items.len > 0);
}
test "renderer - lorem words random" {
std.debug.print("____________________________________________________\n", .{});
std.debug.print("26 - lorem words random\n", .{});
const alloc = testing.allocator;
var ctx = Context.init(alloc);
defer ctx.deinit();
var cache = TemplateCache.init(alloc);
defer cache.deinit();
const renderer = Renderer.init(&ctx, &cache);
const template =
\\{% lorem 6 w true %}
;
var buf = std.ArrayList(u8){};
defer buf.deinit(alloc);
try renderer.renderString(template, buf.writer(alloc));
std.debug.print("OUTPUT:\n\n{s}\n", .{buf.items});
const spaces = std.mem.count(u8, buf.items, " ");
try testing.expect(spaces == 5);
}
test "renderer - lorem paragraphs random" {
std.debug.print("____________________________________________________\n", .{});
std.debug.print("26 - lorem paragraphs random\n", .{});
const alloc = testing.allocator;
var ctx = Context.init(alloc);
defer ctx.deinit();
var cache = TemplateCache.init(alloc);
defer cache.deinit();
const renderer = Renderer.init(&ctx, &cache);
const template =
\\{% lorem 3 p true %}
;
var buf = std.ArrayList(u8){};
defer buf.deinit(alloc);
try renderer.renderString(template, buf.writer(alloc));
std.debug.print("OUTPUT:\n\n{s}\n", .{buf.items});
const spaces = std.mem.count(u8, buf.items, "<p>");
try testing.expect(spaces == 3);
}