131 lines
4.4 KiB
Zig
131 lines
4.4 KiB
Zig
const std = @import("std");
|
|
const temp = @import("temp");
|
|
const zdt = @import("zdt");
|
|
|
|
const Dimensions = @import("../../Dimensions.zig");
|
|
const renderer = @import("../../renderer.zig");
|
|
|
|
pub const api = @import("../../api/root.zig");
|
|
|
|
const template = @import("template/root.zig");
|
|
|
|
const Self = @This();
|
|
|
|
allocator: std.mem.Allocator,
|
|
dimensions: Dimensions,
|
|
|
|
pub fn init(allocator: std.mem.Allocator, dimensions: Dimensions) Self {
|
|
return .{
|
|
.allocator = allocator,
|
|
.dimensions = dimensions,
|
|
};
|
|
}
|
|
|
|
pub const RenderOptions = struct {
|
|
now: *const zdt.Datetime,
|
|
tz: *const zdt.Timezone,
|
|
max_items: usize,
|
|
show_operator: bool,
|
|
};
|
|
|
|
pub fn render(self: *const Self, efa_dm_resp: *const api.efa.models.DmResponse, options: RenderOptions) ![]const u8 {
|
|
const now_local = try options.now.tzConvert(.{ .tz = options.tz });
|
|
|
|
var arena = std.heap.ArenaAllocator.init(self.allocator);
|
|
defer arena.deinit();
|
|
|
|
const departures = try arena.allocator().alloc(template.Departure, efa_dm_resp.stopEvents.len);
|
|
var dep_i: usize = 0;
|
|
for (efa_dm_resp.stopEvents) |dep| {
|
|
const dp_timetable = try zdt.Datetime.fromISO8601(dep.departureTimeBaseTimetable);
|
|
const dp_timetable_local = try dp_timetable.tzConvert(.{ .tz = options.tz });
|
|
|
|
const dp_planned = try zdt.Datetime.fromISO8601(
|
|
dep.departureTimeEstimated orelse dep.departureTimePlanned,
|
|
);
|
|
const dp_planned_local = try dp_planned.tzConvert(.{ .tz = options.tz });
|
|
|
|
const diff_min: i32 = @intFromFloat(dp_planned_local.diff(dp_timetable_local).totalMinutes());
|
|
const on_time: ?u32 = if (diff_min < 0) @abs(diff_min) else null;
|
|
const delayed: ?u32 = if (diff_min > 0) @abs(diff_min) else null;
|
|
|
|
const date: ?[]const u8 = if (dp_planned_local.dayOfYear() != now_local.dayOfYear())
|
|
try std.fmt.allocPrint(arena.allocator(), "{d}.{d}.{d}", .{
|
|
dp_planned_local.day,
|
|
dp_planned_local.month,
|
|
dp_planned_local.year,
|
|
})
|
|
else
|
|
null;
|
|
|
|
var infos: ?[]const []const u8 = null;
|
|
if (dep.hints) |hints| {
|
|
const arr = try arena.allocator().alloc([]const u8, hints.len + 1);
|
|
var i: usize = 0;
|
|
for (hints) |hint| {
|
|
if (std.mem.eql(u8, hint.type, "Timetable"))
|
|
continue;
|
|
|
|
arr[i] = hint.content;
|
|
i += 1;
|
|
}
|
|
|
|
infos = arr[0..i];
|
|
}
|
|
|
|
departures[dep_i] = template.Departure{
|
|
.time = try std.fmt.allocPrint(
|
|
arena.allocator(),
|
|
"{d}:{d:0>2}",
|
|
.{ dp_timetable_local.hour, dp_timetable_local.minute },
|
|
),
|
|
.time_unix = dp_timetable_local.toUnix(.second),
|
|
.line = dep.transportation.name,
|
|
.transportation_product_class = dep.transportation.product.class,
|
|
.operator = if (dep.transportation.operator) |op| op.name else null,
|
|
.destination = dep.transportation.destination.name,
|
|
.platform = dep.location.properties.platform,
|
|
.date = date,
|
|
.on_time = on_time,
|
|
.delayed = delayed,
|
|
.cancelled = dep.isCancelled,
|
|
.information = infos,
|
|
};
|
|
|
|
dep_i += 1;
|
|
}
|
|
|
|
std.mem.sort(
|
|
template.Departure,
|
|
departures,
|
|
{},
|
|
template.Departure.cmpByTimeUnix,
|
|
);
|
|
|
|
const context = template.Context{
|
|
.station = efa_dm_resp.locations[0].name,
|
|
.time = try std.fmt.allocPrint(
|
|
arena.allocator(),
|
|
"{d}:{d:0>2}",
|
|
.{ now_local.hour, now_local.minute },
|
|
),
|
|
.departures = departures[0..@min(departures.len, options.max_items)],
|
|
.show_operator = options.show_operator,
|
|
};
|
|
|
|
const escaped_context = try context.escape(arena.allocator());
|
|
|
|
var tmp_file = try temp.TempFile.create(arena.allocator(), .{
|
|
.pattern = "render-departures-*.html",
|
|
});
|
|
defer tmp_file.deinit();
|
|
const path = try tmp_file.parent_dir.realpathAlloc(arena.allocator(), tmp_file.basename);
|
|
{
|
|
const file = try tmp_file.open(.{ .mode = .write_only });
|
|
defer file.close();
|
|
const writer = file.writer();
|
|
try template.render(&escaped_context, writer.any());
|
|
}
|
|
|
|
return try renderer.render(self.allocator, path, self.dimensions);
|
|
}
|