update: csrf_token and lorem

This commit is contained in:
Lucas F. 2026-01-03 21:00:11 -03:00
parent bbab647430
commit b09da93f0d
3 changed files with 175 additions and 3 deletions

View file

@ -19,6 +19,14 @@ pub const NodeType = enum {
cycle,
firstof,
load,
csrf_token,
lorem,
};
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
};
pub const LoadNode = struct {
@ -147,6 +155,8 @@ pub const Node = struct {
cycle: ?CycleNode = null,
firstof: ?FirstOfNode = null,
load: ?LoadNode = null,
lorem: ?LoremNode = null,
csrf_token: bool = false,
pub fn deinit(self: Node, allocator: std.mem.Allocator) void {
switch (self.type) {
@ -235,6 +245,12 @@ pub const Node = struct {
for (l.libraries) |lib| allocator.free(lib);
allocator.free(l.libraries);
},
.csrf_token => {},
.lorem => if (self.lorem) |l| {
if (l.count) |c| allocator.free(c);
if (l.method) |m| allocator.free(m);
if (l.format) |f| allocator.free(f);
},
}
}
};
@ -1090,6 +1106,48 @@ pub const Parser = struct {
continue;
}
if (std.mem.eql(u8, tag_name, "lorem")) {
const args = node.tag.?.args;
var count: ?[]const u8 = null;
var method: ?[]const u8 = null;
var format: ?[]const u8 = null;
var parts = std.mem.splitScalar(u8, args, ' ');
while (parts.next()) |part| {
const trimmed = std.mem.trim(u8, part, " \t\r\n");
if (trimmed.len == 0) continue;
const num: usize = std.fmt.parseInt(usize, trimmed, 10) catch 0;
if (num > 0) {
std.debug.print("trimmed: {s}\n", .{trimmed});
count = try allocator.dupe(u8, trimmed);
}
if (std.mem.eql(u8, trimmed, "p") or std.mem.eql(u8, trimmed, "w")) {
std.debug.print("trimmed: {s}\n", .{trimmed});
method = try allocator.dupe(u8, trimmed);
} else if (std.mem.eql(u8, trimmed, "html")) {
std.debug.print("trimmed: {s}\n", .{trimmed});
format = try allocator.dupe(u8, trimmed);
}
}
allocator.free(node.tag.?.name);
allocator.free(node.tag.?.args);
try list.append(allocator, Node{
.type = .lorem,
.lorem = .{
.count = count,
.method = method,
.format = format,
},
});
continue;
}
if (std.mem.eql(u8, tag_name, "filter")) {
const filters_raw = node.tag.?.args;
const raw_open = node.tag.?.raw;
@ -1457,6 +1515,25 @@ pub const Parser = struct {
continue;
}
if (std.mem.eql(u8, tag_name, "csrf_token")) {
// Verifica se tem argumentos (não deve ter)
if (node.tag.?.args.len > 0 and !std.mem.allEqual(u8, node.tag.?.args, ' ')) {
return error.InvalidCsrfTokenArgs;
}
allocator.free(node.tag.?.name);
allocator.free(node.tag.?.args);
std.debug.print("3.0 - na real sou um csrf_token\n", .{});
std.debug.print("===================================\n", .{});
try list.append(allocator, Node{
.type = .csrf_token,
.csrf_token = true,
});
continue;
}
// Para tags normais
std.debug.print("===================================\n", .{});
try list.append(allocator, node);

View file

@ -643,3 +643,69 @@ test "parse load com múltiplas" {
try testing.expectEqualStrings("admin_urls", l.libraries[0]);
try testing.expectEqualStrings("static", l.libraries[1]);
}
test "parse csrf_token simples" {
const allocator = testing.allocator;
const template = "<form>{% csrf_token %}</form>";
const nodes = try parser.parse(allocator, template);
defer {
for (nodes) |node| node.deinit(allocator);
allocator.free(nodes);
}
try testing.expectEqual(@as(usize, 3), nodes.len);
try testing.expect(nodes[0].type == .text);
try testing.expectEqualStrings("<form>", nodes[0].text.?.content);
try testing.expect(nodes[1].type == .csrf_token);
try testing.expect(nodes[2].type == .text);
try testing.expectEqualStrings("</form>", nodes[2].text.?.content);
}
test "parse csrf_token sozinho" {
const allocator = testing.allocator;
const template = "{% csrf_token %}";
const nodes = try parser.parse(allocator, template);
defer {
for (nodes) |node| node.deinit(allocator);
allocator.free(nodes);
}
try testing.expectEqual(@as(usize, 1), nodes.len);
try testing.expect(nodes[0].type == .csrf_token);
}
test "parse lorem padrão" {
const allocator = testing.allocator;
const template = "{% lorem %}";
const nodes = try parser.parse(allocator, template);
defer {
for (nodes) |node| node.deinit(allocator);
allocator.free(nodes);
}
try testing.expectEqual(@as(usize, 1), nodes.len);
try testing.expect(nodes[0].type == .lorem);
const l = nodes[0].lorem.?;
try testing.expect(l.count == null);
try testing.expect(l.method == null);
try testing.expect(l.format == null);
}
test "parse lorem com argumentos" {
const allocator = testing.allocator;
const template = "{% lorem 5 p html %}";
const nodes = try parser.parse(allocator, template);
defer {
for (nodes) |node| node.deinit(allocator);
allocator.free(nodes);
}
try testing.expectEqual(@as(usize, 1), nodes.len);
try testing.expect(nodes[0].type == .lorem);
const l = nodes[0].lorem.?;
try testing.expectEqualStrings("5", l.count.?);
try testing.expectEqualStrings("p", l.method.?);
try testing.expectEqualStrings("html", l.format.?);
}

35
todo.md
View file

@ -3,7 +3,7 @@
- [x] autoescape
- [x] block
- [x] comment
- [ ] csrf_token
- [x] csrf_token
- [x] cycle
- [ ] debug
- [x] extends
@ -14,7 +14,7 @@
- [x] ifchanged
- [x] include
- [x] load
- [ ] lorem
- [x] lorem
- [x] now
- [ ] partial
- [ ] partialdef
@ -101,4 +101,33 @@ ___
- [x] cycle — alternar valores em loop
- [x] firstof — fallback de variáveis
- [x] load — para custom tags/filters (futuro)
- [ ] csrf_token — quando tiver web
- [x] csrf_token — quando tiver web
---
## To do
1 - Finalizar o parser — completar as tags que faltam da lista:
- [ ] debug
- [x] lorem
- [ ] partial / partialdef
- [ ] querystring
- [ ] regroup
- [ ] resetcycle
- [ ] templatetag
- [ ] widthratio
2 - projetar o Context com calma:
- Estrutura hierárquica (escopos aninhados para with, for, etc.)
- Suporte a variáveis, listas, structs (models)
- Acesso por ponto (obj.atributo, lista.0, etc.)
- Fallback silencioso ou erro (como no Django)
3 - Renderer — com:
- Resolução de variáveis
- Aplicação de filtros
- Herança com block.super
- Loops com forloop
- Tudo testado
4 -Filtros