4.5 KiB
4.5 KiB
Tags
- autoescape
- block
- comment
- csrf_token
- cycle
- debug
- extends
- filter
- firstof
- for
- if
- ifchanged
- include
- load
- lorem
- now
- partial
- partialdef
- querystring
- regroup
- resetcycle
- spaceless
- templatetag
- url
- verbatim
- widthratio
- with
Filters
- add
- addslashes
- capfirst
- center
- cut
- date
- default
- default_if_none
- dictsort
- dictsortreversed
- divisibleby
- escape
- escapejs
- escapeseq
- filesizeformat
- first
- floatformat
- force_escape
- get_digit
- iriencode
- join
- json_script
- last
- length
- linebreaks
- linebreaksbr
- linenumbers
- ljust
- lower
- make_list
- phone2numeric
- pluralize
- pprint
- random
- rjust
- safe
- safeseq
- slice
- slugify
- stringformat
- striptags
- time
- timesince
- timeuntil
- title
- truncatechars
- [-] truncatechars_html
- truncatewords
- [-] truncatewords_html
- unordered_list
- upper
- urlencode
- urlize
- urlizetrunc
- wordcount
- wordwrap
- yesno
Doing
- filter — super útil (ex.: {{ var|upper }})
- autoescape — segurança importante
- spaceless — remove espaços em branco
- verbatim — como raw
- url — reverse de URLs (quando tiver routing)
- cycle — alternar valores em loop
- firstof — fallback de variáveis
- load — para custom tags/filters (futuro)
- csrf_token — quando tiver web
To do
1 - Finalizar o parser — completar as tags que faltam da lista:
- debug
- 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
na verdade o pushScope não tava não, a versão final que vc me mandou depois que se inspirou no tokamak foi essa:
const std = @import("std");
pub const Value = union(enum) {
null,
bool: bool,
int: i64,
float: f64,
string: []const u8,
list: []const Value,
dict: std.StringHashMapUnmanaged(Value),
pub fn deinit(self: Value) void {
_ = self; // nada — a arena libera tudo
}
};
pub const Context = struct {
arena: std.heap.ArenaAllocator,
map: std.StringHashMapUnmanaged(Value),
pub fn init(child_allocator: std.mem.Allocator) Context {
const arena = std.heap.ArenaAllocator.init(child_allocator);
return .{
.arena = arena,
.map = .{},
};
}
pub fn allocator(self: *Context) std.mem.Allocator {
return self.arena.allocator();
}
pub fn deinit(self: *Context) void {
self.arena.deinit();
}
pub fn set(self: *Context, key: []const u8, value: Value) !void {
const gop = try self.map.getOrPut(self.allocator(), key);
if (gop.found_existing) {
// opcional: deinit value antigo se necessário
// mas como arena libera tudo, não precisa
} else {
gop.key_ptr.* = try self.allocator().dupe(u8, key);
}
gop.value_ptr.* = value;
}
pub fn get(self: *const Context, key: []const u8) ?Value {
return self.map.get(key);
}
};
ao colocar isso ordered_dict: []struct { key: []const u8, val: Value }, vc ferrou em todos os switches de Value que agora tem que prever o que fazer com orderdered_dict
Conclusão sobre o getA melhor abordagem, considerando Zig, é: Manter o get(comptime T: type, key) — é seguro, performático, e em uso real fica natural
const idade: i64 = ctx.get("idade") orelse 0;
const nome: []const u8 = ctx.get("nome") orelse "Desconhecido";
É verboso no teste, mas em código real é claro e seguro.Ou podemos fazer uma versão com optional:
pub fn get(self: *const Context, comptime T: type, key: []const u8) ?T {
const value = self.map.get(key) orelse return null;
return value.as(T) catch null;
}
Aí fica:
const idade = ctx.get(i64, "idade") orelse 0;