update: date filters
This commit is contained in:
parent
002d2b949e
commit
e11d3fb034
2 changed files with 118 additions and 97 deletions
110
src/filters.zig
110
src/filters.zig
|
|
@ -2,11 +2,15 @@ const std = @import("std");
|
||||||
const Value = @import("context.zig").Value;
|
const Value = @import("context.zig").Value;
|
||||||
const std_time = std.time;
|
const std_time = std.time;
|
||||||
|
|
||||||
|
const time = @import("time.zig");
|
||||||
|
|
||||||
pub const FilterError = error{
|
pub const FilterError = error{
|
||||||
InvalidArgument,
|
InvalidArgument,
|
||||||
|
InvalidCharacter,
|
||||||
|
Overflow,
|
||||||
OutOfMemory,
|
OutOfMemory,
|
||||||
UnknownFilter,
|
UnknownFilter,
|
||||||
};
|
} || time.TimeError;
|
||||||
|
|
||||||
const DictEntry = struct {
|
const DictEntry = struct {
|
||||||
key: []const u8,
|
key: []const u8,
|
||||||
|
|
@ -122,35 +126,9 @@ fn filter_cut(alloc: std.mem.Allocator, value: Value, arg: ?Value) FilterError!V
|
||||||
return Value{ .string = try result.toOwnedSlice(alloc) };
|
return Value{ .string = try result.toOwnedSlice(alloc) };
|
||||||
}
|
}
|
||||||
|
|
||||||
// fn filter_date(alloc: std.mem.Allocator, value: Value, arg: ?Value) FilterError!Value {
|
fn filter_date(alloc: std.mem.Allocator, value: Value, arg: ?Value) FilterError!Value {
|
||||||
// // Por enquanto, simples: aceita string ou int (timestamp) e formata com strftime-like
|
return dateTimeToString(alloc, value, arg);
|
||||||
// // Futuro: suporte completo a std.time
|
}
|
||||||
// _ = alloc;
|
|
||||||
// const format = switch (arg orelse Value{ .string = "d/m/Y" }) {
|
|
||||||
// .string => |f| f,
|
|
||||||
// else => "d/m/Y",
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// const timestamp = switch (value) {
|
|
||||||
// .int => |i| @as(i64, i),
|
|
||||||
// .string => |s| std.fmt.parseInt(i64, s, 10) catch 0,
|
|
||||||
// else => 0,
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// // Simulação simples (em produção usar std.time)
|
|
||||||
// const day = @rem(timestamp, 30) + 1;
|
|
||||||
// const month = @rem(timestamp / 30, 12) + 1;
|
|
||||||
// const year = 2026 + @divFloor(timestamp, 360);
|
|
||||||
//
|
|
||||||
// var buf: [64]u8 = undefined;
|
|
||||||
// const formatted = switch (format) {
|
|
||||||
// "d/m/Y" => std.fmt.bufPrint(&buf, "{d:0>2}/{d:0>2}/{d}", .{ day, month, year }) catch "??/??/????",
|
|
||||||
// "Y-m-d" => std.fmt.bufPrint(&buf, "{d}-{d:0>2}-{d:0>2}", .{ year, month, day }) catch "????-??-??",
|
|
||||||
// else => "formato não suportado",
|
|
||||||
// };
|
|
||||||
//
|
|
||||||
// return Value{ .string = try alloc.dupe(u8, formatted) };
|
|
||||||
// }
|
|
||||||
|
|
||||||
fn filter_default(alloc: std.mem.Allocator, value: Value, arg: ?Value) FilterError!Value {
|
fn filter_default(alloc: std.mem.Allocator, value: Value, arg: ?Value) FilterError!Value {
|
||||||
_ = alloc;
|
_ = alloc;
|
||||||
|
|
@ -656,6 +634,12 @@ fn filter_make_list(alloc: std.mem.Allocator, value: Value, arg: ?Value) FilterE
|
||||||
return Value{ .list = try list.toOwnedSlice(alloc) };
|
return Value{ .list = try list.toOwnedSlice(alloc) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn filter_now(alloc: std.mem.Allocator, value: Value, arg: ?Value) FilterError!Value {
|
||||||
|
_ = value;
|
||||||
|
if (arg.?.string.len == 0) return Value{ .string = try time.Time.now().toStringAlloc(alloc, "F d, Y") };
|
||||||
|
return Value{ .string = try time.Time.now().toStringAlloc(alloc, arg.?.string) };
|
||||||
|
}
|
||||||
|
|
||||||
fn filter_phone2numeric(alloc: std.mem.Allocator, value: Value, arg: ?Value) FilterError!Value {
|
fn filter_phone2numeric(alloc: std.mem.Allocator, value: Value, arg: ?Value) FilterError!Value {
|
||||||
_ = arg;
|
_ = arg;
|
||||||
const s = switch (value) {
|
const s = switch (value) {
|
||||||
|
|
@ -959,45 +943,22 @@ fn filter_striptags(alloc: std.mem.Allocator, value: Value, arg: ?Value) FilterE
|
||||||
return Value{ .string = try result.toOwnedSlice(alloc) };
|
return Value{ .string = try result.toOwnedSlice(alloc) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn filter_time(alloc: std.mem.Allocator, value: Value, arg: ?Value) FilterError!Value {
|
||||||
|
return dateTimeToString(alloc, value, arg);
|
||||||
|
}
|
||||||
|
|
||||||
fn filter_timesince(alloc: std.mem.Allocator, value: Value, arg: ?Value) FilterError!Value {
|
fn filter_timesince(alloc: std.mem.Allocator, value: Value, arg: ?Value) FilterError!Value {
|
||||||
_ = arg;
|
const d = time.Time.parse(value.string) catch return value;
|
||||||
const then = switch (value) {
|
const now = time.Time.parse(arg.?.string) catch return value;
|
||||||
.int => |i| @as(i64, i),
|
|
||||||
else => std_time.timestamp(),
|
|
||||||
};
|
|
||||||
|
|
||||||
const now = std_time.timestamp();
|
return Value{ .string = try d.timeSince(alloc, now) };
|
||||||
var diff = now - then;
|
|
||||||
if (diff < 0) diff = -diff;
|
|
||||||
|
|
||||||
if (diff < 60) {
|
|
||||||
return Value{ .string = try alloc.dupe(u8, "menos de um minuto") };
|
|
||||||
} else if (diff < 3600) {
|
|
||||||
const mins = diff / 60;
|
|
||||||
const str = if (mins == 1) "1 minuto" else try std.fmt.allocPrint(alloc, "{d} minutos", .{mins});
|
|
||||||
return Value{ .string = str };
|
|
||||||
} else if (diff < 86400) {
|
|
||||||
const hours = diff / 3600;
|
|
||||||
const str = if (hours == 1) "1 hora" else try std.fmt.allocPrint(alloc, "{d} horas", .{hours});
|
|
||||||
return Value{ .string = str };
|
|
||||||
} else {
|
|
||||||
const days = diff / 86400;
|
|
||||||
const str = if (days == 1) "1 dia" else try std.fmt.allocPrint(alloc, "{d} dias", .{days});
|
|
||||||
return Value{ .string = str };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn filter_timeuntil(alloc: std.mem.Allocator, value: Value, arg: ?Value) FilterError!Value {
|
fn filter_timeuntil(alloc: std.mem.Allocator, value: Value, arg: ?Value) FilterError!Value {
|
||||||
_ = arg;
|
const d = time.Time.parse(value.string) catch return value;
|
||||||
// Reutiliza timesince, mas com sinal invertido
|
const now = time.Time.parse(arg.?.string) catch return value;
|
||||||
const future = switch (value) {
|
|
||||||
.int => |i| @as(i64, i),
|
|
||||||
else => std_time.timestamp(),
|
|
||||||
};
|
|
||||||
|
|
||||||
const fake_past = Value{ .int = std_time.timestamp() };
|
return Value{ .string = try now.timeSince(alloc, d) };
|
||||||
const since = try filter_timesince(alloc, fake_past, Value{ .int = future });
|
|
||||||
return since;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn filter_title(alloc: std.mem.Allocator, value: Value, arg: ?Value) FilterError!Value {
|
fn filter_title(alloc: std.mem.Allocator, value: Value, arg: ?Value) FilterError!Value {
|
||||||
|
|
@ -1318,6 +1279,20 @@ fn filter_yesno(alloc: std.mem.Allocator, value: Value, arg: ?Value) FilterError
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== AUX FUNCTIONS ====================
|
// ==================== AUX FUNCTIONS ====================
|
||||||
|
pub fn dateTimeToString(allocator: std.mem.Allocator, value: Value, arg: ?Value) FilterError!Value {
|
||||||
|
if (value.string.len > 0) {
|
||||||
|
const t: time.Time = try time.Time.parse(value.string);
|
||||||
|
const arg_str: Value = arg orelse Value{ .string = "F d, Y" };
|
||||||
|
const format_str: []const u8 = arg_str.string;
|
||||||
|
|
||||||
|
const result: []const u8 = try t.toStringAlloc(allocator, format_str);
|
||||||
|
if (result.len > 0) {
|
||||||
|
return Value{ .string = result };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn capFirst(allocator: std.mem.Allocator, input: []const u8) ![]const u8 {
|
pub fn capFirst(allocator: std.mem.Allocator, input: []const u8) ![]const u8 {
|
||||||
if (input.len == 0) return "";
|
if (input.len == 0) return "";
|
||||||
|
|
||||||
|
|
@ -1462,7 +1437,7 @@ pub const builtin_filters = std.StaticStringMap(*const FilterFn).initComptime(.{
|
||||||
.{ "capfirst", &filter_capfirst },
|
.{ "capfirst", &filter_capfirst },
|
||||||
.{ "center", &filter_center },
|
.{ "center", &filter_center },
|
||||||
.{ "cut", &filter_cut },
|
.{ "cut", &filter_cut },
|
||||||
// .{ "date", &filter_date },
|
.{ "date", &filter_date },
|
||||||
.{ "default", &filter_default },
|
.{ "default", &filter_default },
|
||||||
.{ "default_if_none", &filter_default_if_none },
|
.{ "default_if_none", &filter_default_if_none },
|
||||||
.{ "dictsort", &filter_dictsort },
|
.{ "dictsort", &filter_dictsort },
|
||||||
|
|
@ -1487,6 +1462,7 @@ pub const builtin_filters = std.StaticStringMap(*const FilterFn).initComptime(.{
|
||||||
.{ "ljust", &filter_ljust },
|
.{ "ljust", &filter_ljust },
|
||||||
.{ "lower", &filter_lower },
|
.{ "lower", &filter_lower },
|
||||||
.{ "make_list", &filter_make_list },
|
.{ "make_list", &filter_make_list },
|
||||||
|
.{ "now", &filter_now },
|
||||||
.{ "phone2numeric", &filter_phone2numeric },
|
.{ "phone2numeric", &filter_phone2numeric },
|
||||||
.{ "pluralize", &filter_pluralize },
|
.{ "pluralize", &filter_pluralize },
|
||||||
.{ "pprint", &filter_pprint },
|
.{ "pprint", &filter_pprint },
|
||||||
|
|
@ -1498,9 +1474,9 @@ pub const builtin_filters = std.StaticStringMap(*const FilterFn).initComptime(.{
|
||||||
.{ "slugify", &filter_slugify },
|
.{ "slugify", &filter_slugify },
|
||||||
.{ "stringformat", &filter_stringformat },
|
.{ "stringformat", &filter_stringformat },
|
||||||
.{ "striptags", &filter_striptags },
|
.{ "striptags", &filter_striptags },
|
||||||
// .{ "time", &filter_time },
|
.{ "time", &filter_time },
|
||||||
// .{ "timesince", &filter_timesince },
|
.{ "timesince", &filter_timesince },
|
||||||
// .{ "timeuntil", &filter_timeuntil },
|
.{ "timeuntil", &filter_timeuntil },
|
||||||
.{ "title", &filter_title },
|
.{ "title", &filter_title },
|
||||||
.{ "truncatechars", &filter_truncatechars },
|
.{ "truncatechars", &filter_truncatechars },
|
||||||
.{ "truncatechars_html", &filter_truncatechars_html },
|
.{ "truncatechars_html", &filter_truncatechars_html },
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,11 @@ const testing = std.testing;
|
||||||
const Value = @import("context.zig").Value;
|
const Value = @import("context.zig").Value;
|
||||||
const Context = @import("context.zig").Context;
|
const Context = @import("context.zig").Context;
|
||||||
const builtin_filters = @import("filters.zig").builtin_filters;
|
const builtin_filters = @import("filters.zig").builtin_filters;
|
||||||
const FilterError = @import("filters.zig").FilterError;
|
const filter = @import("filters.zig");
|
||||||
|
const FilterError = filter.FilterError;
|
||||||
|
const time = @import("time.zig");
|
||||||
const std_time = std.time;
|
const std_time = std.time;
|
||||||
|
const RelativeDelta = @import("delta.zig").RelativeDelta;
|
||||||
|
|
||||||
test "filters upper/lower, capfirst" {
|
test "filters upper/lower, capfirst" {
|
||||||
std.debug.print("____________________________________________________\n", .{});
|
std.debug.print("____________________________________________________\n", .{});
|
||||||
|
|
@ -180,7 +183,6 @@ test "builtin filters - join" {
|
||||||
try ctx.set("mixed", mixed);
|
try ctx.set("mixed", mixed);
|
||||||
const mixed_val = ctx.get("mixed").?;
|
const mixed_val = ctx.get("mixed").?;
|
||||||
|
|
||||||
|
|
||||||
const default_join = try join(ctx.allocator(), list_val, null);
|
const default_join = try join(ctx.allocator(), list_val, null);
|
||||||
const custom_join = try join(ctx.allocator(), list_val, sep_dash);
|
const custom_join = try join(ctx.allocator(), list_val, sep_dash);
|
||||||
const mixed_join = try join(ctx.allocator(), mixed_val, null);
|
const mixed_join = try join(ctx.allocator(), mixed_val, null);
|
||||||
|
|
@ -558,9 +560,9 @@ test "builtin filters - pluralize" {
|
||||||
// try testing.expectEqualStrings("", zero.string);
|
// try testing.expectEqualStrings("", zero.string);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "builtin filters - addslashes, center, date" {
|
test "builtin filters - addslashes, center" {
|
||||||
std.debug.print("____________________________________________________\n", .{});
|
std.debug.print("____________________________________________________\n", .{});
|
||||||
std.debug.print("24 - addslashes, center, date\n", .{});
|
std.debug.print("24 - addslashes, center\n", .{});
|
||||||
|
|
||||||
const alloc = testing.allocator;
|
const alloc = testing.allocator;
|
||||||
var ctx = Context.init(alloc);
|
var ctx = Context.init(alloc);
|
||||||
|
|
@ -568,21 +570,19 @@ test "builtin filters - addslashes, center, date" {
|
||||||
|
|
||||||
try ctx.set("quote", "He's a good boy");
|
try ctx.set("quote", "He's a good boy");
|
||||||
try ctx.set("texto", "zig");
|
try ctx.set("texto", "zig");
|
||||||
|
try ctx.set("time", time.Time.new(2026, 1, 1, 0, 0, 0));
|
||||||
|
|
||||||
const v_quote = ctx.get("quote").?;
|
const v_quote = ctx.get("quote").?;
|
||||||
const v_texto = ctx.get("texto").?;
|
const v_texto = ctx.get("texto").?;
|
||||||
|
|
||||||
const addslashes = builtin_filters.get("addslashes").?;
|
const addslashes = builtin_filters.get("addslashes").?;
|
||||||
const center = builtin_filters.get("center").?;
|
const center = builtin_filters.get("center").?;
|
||||||
// const date = builtin_filters.get("date").?;
|
|
||||||
|
|
||||||
const slashed = try addslashes(ctx.allocator(), v_quote, null);
|
const slashed = try addslashes(ctx.allocator(), v_quote, null);
|
||||||
const centered = try center(ctx.allocator(), v_texto, Value{ .int = 10 });
|
const centered = try center(ctx.allocator(), v_texto, Value{ .int = 10 });
|
||||||
// const formatted = try date(ctx.allocator(), Value{ .int = 0 }, Value{ .string = "Y-m-d" });
|
|
||||||
|
|
||||||
try testing.expectEqualStrings("He\\'s a good boy", slashed.string);
|
try testing.expectEqualStrings("He\\'s a good boy", slashed.string);
|
||||||
try testing.expectEqualStrings(" zig ", centered.string);
|
try testing.expectEqualStrings(" zig ", centered.string);
|
||||||
// try testing.expect(std.mem.startsWith(u8, formatted.string, "2026"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test "builtin filters - dictsort and dictsortreversed" {
|
test "builtin filters - dictsort and dictsortreversed" {
|
||||||
|
|
@ -760,27 +760,64 @@ test "builtin filters - random, safeseq" {
|
||||||
try testing.expect(safe == .list);
|
try testing.expect(safe == .list);
|
||||||
}
|
}
|
||||||
|
|
||||||
// test "builtin filters - date, time, timesince, timeuntil" {
|
test "builtin filters - date, now, time, timesince, timeuntil" {
|
||||||
// const alloc = testing.allocator;
|
std.debug.print("____________________________________________________\n", .{});
|
||||||
// var ctx = Context.init(alloc);
|
std.debug.print("31 - date, now, time, timesince, timeuntil\n", .{});
|
||||||
// defer ctx.deinit();
|
|
||||||
//
|
const alloc = testing.allocator;
|
||||||
// const now = std_time.timestamp();
|
var ctx = Context.init(alloc);
|
||||||
//
|
defer ctx.deinit();
|
||||||
// // const date = builtin_filters.get("date").?;
|
|
||||||
// const time = builtin_filters.get("time").?;
|
const times = [_]time.Time{
|
||||||
// const timesince = builtin_filters.get("timesince").?;
|
time.Time.new(2026, 1, 2, 13, 15, 10),
|
||||||
// // const timeuntil = builtin_filters.get("timeuntil").?;
|
time.Time.new(2026, 1, 6, 19, 25, 0),
|
||||||
//
|
time.Time.new(2026, 1, 2, 13, 35, 0),
|
||||||
// // const d = try date(ctx.allocator(), Value{ .int = now }, Value{ .string = "d/m/Y" });
|
time.Time.new(2026, 1, 2, 13, 15, 19),
|
||||||
// const t = try time(ctx.allocator(), Value{ .int = now }, Value{ .string = "H:i" });
|
time.Time.new(2025, 1, 2, 13, 15,19),
|
||||||
//
|
time.Time.new(2024, 7, 5, 19, 4, 2),
|
||||||
// // try testing.expect(d.string.len > 0);
|
};
|
||||||
// try testing.expect(t.string.len > 0);
|
|
||||||
//
|
const date_filter = builtin_filters.get("date").?;
|
||||||
// const since = try timesince(ctx.allocator(), Value{ .int = now - 3600 }, null);
|
const now_filter = builtin_filters.get("now").?;
|
||||||
// try testing.expect(std.mem.indexOf(u8, since.string, "hora") != null);
|
const time_filter = builtin_filters.get("time").?;
|
||||||
// }
|
const timesince_filter = builtin_filters.get("timesince").?;
|
||||||
|
const timeuntil_filer = builtin_filters.get("timeuntil").?;
|
||||||
|
|
||||||
|
try ctx.set("dates", times);
|
||||||
|
const dates = ctx.get("dates").?;
|
||||||
|
|
||||||
|
const date_formated = try date_filter(ctx.allocator(), dates.list[0], Value{ .string = "Y-m-d" });
|
||||||
|
const now_formated = try now_filter(ctx.allocator(), dates.list[0], Value{ .string = "Y-m-d" });
|
||||||
|
const time_formated = try time_filter(ctx.allocator(), dates.list[0], Value{ .string = "H:i:s" });
|
||||||
|
const timesince_formated_1 = try timesince_filter(ctx.allocator(), dates.list[0], dates.list[1]);
|
||||||
|
const timesince_formated_2 = try timesince_filter(ctx.allocator(), dates.list[0], dates.list[2]);
|
||||||
|
const timesince_formated_3 = try timesince_filter(ctx.allocator(), dates.list[0], dates.list[3]);
|
||||||
|
const timesince_formated_4 = try timesince_filter(ctx.allocator(), dates.list[4], dates.list[0]);
|
||||||
|
const timesince_formated_5 = try timesince_filter(ctx.allocator(), dates.list[5], dates.list[0]);
|
||||||
|
|
||||||
|
const timeuntil_formated_1 = try timeuntil_filer(ctx.allocator(), dates.list[1], dates.list[0]);
|
||||||
|
const timeuntil_formated_2 = try timeuntil_filer(ctx.allocator(), dates.list[2], dates.list[0]);
|
||||||
|
const timeuntil_formated_3 = try timeuntil_filer(ctx.allocator(), dates.list[3], dates.list[0]);
|
||||||
|
const timeuntil_formated_4 = try timeuntil_filer(ctx.allocator(), dates.list[0], dates.list[4]);
|
||||||
|
const timeuntil_formated_5 = try timeuntil_filer(ctx.allocator(), dates.list[0], dates.list[5]);
|
||||||
|
|
||||||
|
try testing.expectEqualStrings("2026-01-02", date_formated.string);
|
||||||
|
try testing.expect(isDateFormat(now_formated.string));
|
||||||
|
try testing.expectEqualStrings("13:15:10", time_formated.string);
|
||||||
|
|
||||||
|
try testing.expectEqualStrings("4 days, 6 hours",timesince_formated_1.string);
|
||||||
|
try testing.expectEqualStrings("19 minutes",timesince_formated_2.string);
|
||||||
|
try testing.expectEqualStrings("0 minutes",timesince_formated_3.string);
|
||||||
|
try testing.expectEqualStrings("11 months, 4 weeks",timesince_formated_4.string);
|
||||||
|
try testing.expectEqualStrings("1 year, 5 months",timesince_formated_5.string);
|
||||||
|
|
||||||
|
try testing.expectEqualStrings("4 days, 6 hours",timeuntil_formated_1.string);
|
||||||
|
try testing.expectEqualStrings("19 minutes",timeuntil_formated_2.string);
|
||||||
|
try testing.expectEqualStrings("0 minutes",timeuntil_formated_3.string);
|
||||||
|
try testing.expectEqualStrings("11 months, 4 weeks",timeuntil_formated_4.string);
|
||||||
|
try testing.expectEqualStrings("1 year, 5 months",timeuntil_formated_5.string);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
test "builtin filters - urlize, urlizetrunc, wordwrap, unordered_list" {
|
test "builtin filters - urlize, urlizetrunc, wordwrap, unordered_list" {
|
||||||
std.debug.print("____________________________________________________\n", .{});
|
std.debug.print("____________________________________________________\n", .{});
|
||||||
|
|
@ -807,7 +844,6 @@ test "builtin filters - urlize, urlizetrunc, wordwrap, unordered_list" {
|
||||||
const long_text = "Este é um texto muito longo que precisa ser quebrado em várias linhas para caber na largura especificada";
|
const long_text = "Este é um texto muito longo que precisa ser quebrado em várias linhas para caber na largura especificada";
|
||||||
const wrapped = try wordwrap(ctx.allocator(), Value{ .string = long_text }, Value{ .int = 20 });
|
const wrapped = try wordwrap(ctx.allocator(), Value{ .string = long_text }, Value{ .int = 20 });
|
||||||
try testing.expect(std.mem.indexOf(u8, wrapped.string, "\n") != null);
|
try testing.expect(std.mem.indexOf(u8, wrapped.string, "\n") != null);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test "builtin filters - unordered_list" {
|
test "builtin filters - unordered_list" {
|
||||||
|
|
@ -818,7 +854,6 @@ test "builtin filters - unordered_list" {
|
||||||
var ctx = Context.init(alloc);
|
var ctx = Context.init(alloc);
|
||||||
defer ctx.deinit();
|
defer ctx.deinit();
|
||||||
|
|
||||||
|
|
||||||
const list = [_]Value{ Value{ .string = "item1" }, Value{ .string = "item2" } };
|
const list = [_]Value{ Value{ .string = "item1" }, Value{ .string = "item2" } };
|
||||||
try ctx.set("lista", list);
|
try ctx.set("lista", list);
|
||||||
|
|
||||||
|
|
@ -835,3 +870,13 @@ test "builtin filters - unordered_list" {
|
||||||
;
|
;
|
||||||
try testing.expectEqualStrings(expected, ul.string);
|
try testing.expectEqualStrings(expected, ul.string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn isDateFormat(txt: []const u8) bool {
|
||||||
|
if (txt.len != 10) return false;
|
||||||
|
if (txt[4] != '-' or txt[7] != '-') return false;
|
||||||
|
for (txt, 0..) |c, i| {
|
||||||
|
if (i == 4 or i == 7) continue;
|
||||||
|
if (!std.ascii.isDigit(c)) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue