Restructure render
90
src/eink_feed_render/api/efa/models.zig
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
pub const TransportationProductClass = enum(u32) {
|
||||||
|
zug = 0,
|
||||||
|
s_bahn = 1,
|
||||||
|
u_bahn = 2,
|
||||||
|
stadtbahn = 3,
|
||||||
|
strassen_trambahn = 4,
|
||||||
|
stadtbus = 5,
|
||||||
|
regionalbus = 6,
|
||||||
|
schnellbus = 7,
|
||||||
|
seil_zahnradbahn = 8,
|
||||||
|
schiff = 9,
|
||||||
|
anruf_sammel_taxi = 10,
|
||||||
|
sonstige = 11,
|
||||||
|
flugzeug = 12,
|
||||||
|
zug_nv = 13,
|
||||||
|
zug_fv = 14,
|
||||||
|
zug_fv_m_zuschlag = 15,
|
||||||
|
zug_fv_m_spez_fpr = 16,
|
||||||
|
sev = 17,
|
||||||
|
zug_shuttle = 18,
|
||||||
|
buergerbus = 19,
|
||||||
|
rufbus_liniengebunden = 20,
|
||||||
|
rufbus = 21,
|
||||||
|
_,
|
||||||
|
|
||||||
|
pub fn toString(self: TransportationProductClass) ?[]const u8 {
|
||||||
|
return switch (self) {
|
||||||
|
.zug => "Zug",
|
||||||
|
.s_bahn => "S-Bahn",
|
||||||
|
.u_bahn => "U-Bahn",
|
||||||
|
.stadtbahn => "Stadtbahn",
|
||||||
|
.strassen_trambahn => "Straßen-/Trambahn",
|
||||||
|
.stadtbus => "Stadtbus",
|
||||||
|
.regionalbus => "Regionalbus",
|
||||||
|
.schnellbus => "Schnellbus",
|
||||||
|
.seil_zahnradbahn => "Seil-/Zahnradbahn",
|
||||||
|
.schiff => "Schiff",
|
||||||
|
.anruf_sammel_taxi => "Anruf-Sammel-Taxi",
|
||||||
|
.sonstige => "Sonstige",
|
||||||
|
.flugzeug => "Flugzeug",
|
||||||
|
.zug_nv => "Zug (Nahverkehr)",
|
||||||
|
.zug_fv => "Zug (Fernverkehr)",
|
||||||
|
.zug_fv_m_zuschlag => "Zug (Fernverkehr) mit Zuschlag",
|
||||||
|
.zug_fv_m_spez_fpr => "Zug (Fernverkehr) mit spezifischer Fpr",
|
||||||
|
.sev => "SEV Schnienenersatzverkehr",
|
||||||
|
.zug_shuttle => "Zug Shuttle",
|
||||||
|
.buergerbus => "Bürgerbus",
|
||||||
|
.rufbus_liniengebunden => "Rufbus (liniengebunden)",
|
||||||
|
.rufbus => "Rufbus",
|
||||||
|
_ => null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Location = struct {
|
||||||
|
id: []const u8,
|
||||||
|
name: []const u8,
|
||||||
|
properties: struct {
|
||||||
|
platform: ?[]const u8 = null,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const StopEvent = struct {
|
||||||
|
location: Location,
|
||||||
|
departureTimePlanned: []const u8,
|
||||||
|
departureTimeBaseTimetable: []const u8,
|
||||||
|
departureTimeEstimated: ?[]const u8 = null,
|
||||||
|
transportation: struct {
|
||||||
|
name: []const u8,
|
||||||
|
product: struct {
|
||||||
|
class: TransportationProductClass,
|
||||||
|
},
|
||||||
|
operator: ?struct {
|
||||||
|
name: []const u8,
|
||||||
|
} = null,
|
||||||
|
destination: struct {
|
||||||
|
name: []const u8,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
hints: ?[]const struct {
|
||||||
|
content: []const u8,
|
||||||
|
type: []const u8,
|
||||||
|
} = null,
|
||||||
|
isCancelled: bool = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const DmResponse = struct {
|
||||||
|
locations: []const Location,
|
||||||
|
stopEvents: []const StopEvent,
|
||||||
|
};
|
|
@ -2,99 +2,10 @@ const std = @import("std");
|
||||||
|
|
||||||
const escaper = @import("../../escaper.zig");
|
const escaper = @import("../../escaper.zig");
|
||||||
|
|
||||||
|
pub const models = @import("models.zig");
|
||||||
|
|
||||||
const log = std.log.scoped(.efa);
|
const log = std.log.scoped(.efa);
|
||||||
|
|
||||||
pub const ApiTransportationProductClass = enum(u32) {
|
|
||||||
zug = 0,
|
|
||||||
s_bahn = 1,
|
|
||||||
u_bahn = 2,
|
|
||||||
stadtbahn = 3,
|
|
||||||
strassen_trambahn = 4,
|
|
||||||
stadtbus = 5,
|
|
||||||
regionalbus = 6,
|
|
||||||
schnellbus = 7,
|
|
||||||
seil_zahnradbahn = 8,
|
|
||||||
schiff = 9,
|
|
||||||
anruf_sammel_taxi = 10,
|
|
||||||
sonstige = 11,
|
|
||||||
flugzeug = 12,
|
|
||||||
zug_nv = 13,
|
|
||||||
zug_fv = 14,
|
|
||||||
zug_fv_m_zuschlag = 15,
|
|
||||||
zug_fv_m_spez_fpr = 16,
|
|
||||||
sev = 17,
|
|
||||||
zug_shuttle = 18,
|
|
||||||
buergerbus = 19,
|
|
||||||
rufbus_liniengebunden = 20,
|
|
||||||
rufbus = 21,
|
|
||||||
_,
|
|
||||||
|
|
||||||
pub fn toString(self: ApiTransportationProductClass) ?[]const u8 {
|
|
||||||
return switch (self) {
|
|
||||||
.zug => "Zug",
|
|
||||||
.s_bahn => "S-Bahn",
|
|
||||||
.u_bahn => "U-Bahn",
|
|
||||||
.stadtbahn => "Stadtbahn",
|
|
||||||
.strassen_trambahn => "Straßen-/Trambahn",
|
|
||||||
.stadtbus => "Stadtbus",
|
|
||||||
.regionalbus => "Regionalbus",
|
|
||||||
.schnellbus => "Schnellbus",
|
|
||||||
.seil_zahnradbahn => "Seil-/Zahnradbahn",
|
|
||||||
.schiff => "Schiff",
|
|
||||||
.anruf_sammel_taxi => "Anruf-Sammel-Taxi",
|
|
||||||
.sonstige => "Sonstige",
|
|
||||||
.flugzeug => "Flugzeug",
|
|
||||||
.zug_nv => "Zug (Nahverkehr)",
|
|
||||||
.zug_fv => "Zug (Fernverkehr)",
|
|
||||||
.zug_fv_m_zuschlag => "Zug (Fernverkehr) mit Zuschlag",
|
|
||||||
.zug_fv_m_spez_fpr => "Zug (Fernverkehr) mit spezifischer Fpr",
|
|
||||||
.sev => "SEV Schnienenersatzverkehr",
|
|
||||||
.zug_shuttle => "Zug Shuttle",
|
|
||||||
.buergerbus => "Bürgerbus",
|
|
||||||
.rufbus_liniengebunden => "Rufbus (liniengebunden)",
|
|
||||||
.rufbus => "Rufbus",
|
|
||||||
_ => null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const ApiLocation = struct {
|
|
||||||
id: []const u8,
|
|
||||||
name: []const u8,
|
|
||||||
properties: struct {
|
|
||||||
platform: ?[]const u8 = null,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const ApiStopEvent = struct {
|
|
||||||
location: ApiLocation,
|
|
||||||
departureTimePlanned: []const u8,
|
|
||||||
departureTimeBaseTimetable: []const u8,
|
|
||||||
departureTimeEstimated: ?[]const u8 = null,
|
|
||||||
transportation: struct {
|
|
||||||
name: []const u8,
|
|
||||||
product: struct {
|
|
||||||
class: ApiTransportationProductClass,
|
|
||||||
},
|
|
||||||
operator: ?struct {
|
|
||||||
name: []const u8,
|
|
||||||
} = null,
|
|
||||||
destination: struct {
|
|
||||||
name: []const u8,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
hints: ?[]const struct {
|
|
||||||
content: []const u8,
|
|
||||||
type: []const u8,
|
|
||||||
} = null,
|
|
||||||
isCancelled: bool = false,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const DmResponse = struct {
|
|
||||||
locations: []const ApiLocation,
|
|
||||||
stopEvents: []const ApiStopEvent,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const Client = struct {
|
pub const Client = struct {
|
||||||
pub const Error = error{
|
pub const Error = error{
|
||||||
InvalidResponse,
|
InvalidResponse,
|
||||||
|
@ -170,7 +81,7 @@ pub const Client = struct {
|
||||||
try std.io.getStdOut().writeAll(body);
|
try std.io.getStdOut().writeAll(body);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getDepartures(self: *const Self, stop_id: []const u8) !std.json.Parsed(DmResponse) {
|
pub fn getDepartures(self: *const Self, stop_id: []const u8) !std.json.Parsed(models.DmResponse) {
|
||||||
const escaped_stop_id = try escaper.escapeUriComponent(self.allocator, stop_id);
|
const escaped_stop_id = try escaper.escapeUriComponent(self.allocator, stop_id);
|
||||||
defer if (escaped_stop_id) |s| self.allocator.free(s);
|
defer if (escaped_stop_id) |s| self.allocator.free(s);
|
||||||
const url_stop_id = escaped_stop_id orelse stop_id;
|
const url_stop_id = escaped_stop_id orelse stop_id;
|
||||||
|
@ -204,7 +115,7 @@ pub const Client = struct {
|
||||||
defer self.allocator.free(body);
|
defer self.allocator.free(body);
|
||||||
|
|
||||||
const response = try std.json.parseFromSlice(
|
const response = try std.json.parseFromSlice(
|
||||||
DmResponse,
|
models.DmResponse,
|
||||||
self.allocator,
|
self.allocator,
|
||||||
body,
|
body,
|
||||||
.{ .allocate = .alloc_always, .ignore_unknown_fields = true },
|
.{ .allocate = .alloc_always, .ignore_unknown_fields = true },
|
1
src/eink_feed_render/api/root.zig
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub const efa = @import("efa/root.zig");
|
|
@ -5,7 +5,7 @@ const zdt = @import("zdt");
|
||||||
const Dimensions = @import("../../Dimensions.zig");
|
const Dimensions = @import("../../Dimensions.zig");
|
||||||
const renderer = @import("../../renderer.zig");
|
const renderer = @import("../../renderer.zig");
|
||||||
|
|
||||||
pub const efa = @import("efa.zig");
|
pub const api = @import("../../api/root.zig");
|
||||||
|
|
||||||
const template = @import("template/root.zig");
|
const template = @import("template/root.zig");
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ pub const RenderOptions = struct {
|
||||||
show_operator: bool,
|
show_operator: bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn render(self: *const Self, efa_dm_resp: *const efa.DmResponse, options: RenderOptions) ![]const u8 {
|
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 });
|
const now_local = try options.now.tzConvert(.{ .tz = options.tz });
|
||||||
|
|
||||||
var arena = std.heap.ArenaAllocator.init(self.allocator);
|
var arena = std.heap.ArenaAllocator.init(self.allocator);
|
||||||
|
|
Before Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1 KiB |
Before Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.4 KiB |
|
@ -1,8 +1,31 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const efa = @import("../../efa.zig");
|
const api = @import("../../../../api/root.zig");
|
||||||
|
const embed = @import("../../../../embed.zig");
|
||||||
|
|
||||||
pub fn iconFromTransportationProductClass(class: efa.ApiTransportationProductClass) ?[]const u8 {
|
pub const data = embed.embedBase64Files(
|
||||||
|
(struct {
|
||||||
|
fn inner(comptime path: []const u8) []const u8 {
|
||||||
|
return @embedFile(path);
|
||||||
|
}
|
||||||
|
}).inner,
|
||||||
|
"image/svg+xml",
|
||||||
|
"svg",
|
||||||
|
"svg",
|
||||||
|
&.{
|
||||||
|
"train",
|
||||||
|
"subway",
|
||||||
|
"tram",
|
||||||
|
"bus",
|
||||||
|
"shuttle",
|
||||||
|
"gondola",
|
||||||
|
"flight",
|
||||||
|
"boat",
|
||||||
|
"no_transfer",
|
||||||
|
},
|
||||||
|
){};
|
||||||
|
|
||||||
|
pub fn iconFromTransportationProductClass(class: api.efa.models.TransportationProductClass) ?embed.EncodedFile {
|
||||||
return switch (class) {
|
return switch (class) {
|
||||||
.zug,
|
.zug,
|
||||||
.s_bahn,
|
.s_bahn,
|
||||||
|
@ -11,11 +34,11 @@ pub fn iconFromTransportationProductClass(class: efa.ApiTransportationProductCla
|
||||||
.zug_fv_m_zuschlag,
|
.zug_fv_m_zuschlag,
|
||||||
.zug_fv_m_spez_fpr,
|
.zug_fv_m_spez_fpr,
|
||||||
.zug_shuttle,
|
.zug_shuttle,
|
||||||
=> train,
|
=> data.train,
|
||||||
|
|
||||||
.u_bahn => subway,
|
.u_bahn => data.subway,
|
||||||
|
|
||||||
.stadtbahn, .strassen_trambahn => tram,
|
.stadtbahn, .strassen_trambahn => data.tram,
|
||||||
|
|
||||||
.stadtbus,
|
.stadtbus,
|
||||||
.regionalbus,
|
.regionalbus,
|
||||||
|
@ -24,40 +47,18 @@ pub fn iconFromTransportationProductClass(class: efa.ApiTransportationProductCla
|
||||||
.rufbus_liniengebunden,
|
.rufbus_liniengebunden,
|
||||||
.rufbus,
|
.rufbus,
|
||||||
.anruf_sammel_taxi,
|
.anruf_sammel_taxi,
|
||||||
=> bus,
|
=> data.bus,
|
||||||
|
|
||||||
.seil_zahnradbahn => gondola,
|
.seil_zahnradbahn => data.gondola,
|
||||||
|
|
||||||
.schiff => boat,
|
.schiff => data.boat,
|
||||||
|
|
||||||
.sonstige => null,
|
.sonstige => null,
|
||||||
|
|
||||||
.flugzeug => flight,
|
.flugzeug => data.flight,
|
||||||
|
|
||||||
.sev => no_transfer,
|
.sev => data.no_transfer,
|
||||||
|
|
||||||
_ => null,
|
_ => null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encodePng(comptime path: []const u8) []const u8 {
|
|
||||||
const raw = @embedFile(path);
|
|
||||||
const len = std.base64.standard.Encoder.calcSize(raw.len);
|
|
||||||
var buf: [len]u8 = undefined;
|
|
||||||
{
|
|
||||||
@setEvalBranchQuota(1_000_000);
|
|
||||||
_ = std.base64.standard.Encoder.encode(&buf, raw);
|
|
||||||
}
|
|
||||||
const final = buf;
|
|
||||||
return &final;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const train = encodePng("img/train.png");
|
|
||||||
pub const subway = encodePng("img/subway.png");
|
|
||||||
pub const tram = encodePng("img/tram.png");
|
|
||||||
pub const bus = encodePng("img/bus.png");
|
|
||||||
pub const shuttle = encodePng("img/shuttle.png");
|
|
||||||
pub const gondola = encodePng("img/gondola.png");
|
|
||||||
pub const flight = encodePng("img/flight.png");
|
|
||||||
pub const boat = encodePng("img/boat.png");
|
|
||||||
pub const no_transfer = encodePng("img/no_transfer.png");
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const escaper = @import("../../../escaper.zig");
|
const escaper = @import("../../../escaper.zig");
|
||||||
const efa = @import("../efa.zig");
|
const api = @import("../../../api/root.zig");
|
||||||
|
|
||||||
const icons = @import("icons/root.zig");
|
const icons = @import("icons/root.zig");
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ pub const Departure = struct {
|
||||||
time: []const u8,
|
time: []const u8,
|
||||||
time_unix: i128,
|
time_unix: i128,
|
||||||
line: []const u8,
|
line: []const u8,
|
||||||
transportation_product_class: efa.ApiTransportationProductClass,
|
transportation_product_class: api.efa.models.TransportationProductClass,
|
||||||
operator: ?[]const u8,
|
operator: ?[]const u8,
|
||||||
destination: []const u8,
|
destination: []const u8,
|
||||||
platform: ?[]const u8,
|
platform: ?[]const u8,
|
||||||
|
@ -188,13 +188,16 @@ pub fn render(context: *const Context, writer: anytype) !void {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (icons.iconFromTransportationProductClass(departure.transportation_product_class)) |img| {
|
if (icons.iconFromTransportationProductClass(departure.transportation_product_class)) |file| {
|
||||||
try writer.print(
|
try writer.print(
|
||||||
\\ <td>
|
\\ <td>
|
||||||
\\ <img class="icon" src="data:image/png;charset=utf-8;base64,{[img]s}" />
|
\\ <img class="icon" src="data:{[content_type]s};charset=utf-8;base64,{[data]s}" />
|
||||||
\\ </td>
|
\\ </td>
|
||||||
,
|
,
|
||||||
.{ .img = img },
|
.{
|
||||||
|
.content_type = file.content_type,
|
||||||
|
.data = file.data,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
try writer.writeAll(
|
try writer.writeAll(
|
||||||
|
|
67
src/eink_feed_render/embed.zig
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub const EncodedFile = struct {
|
||||||
|
content_type: []const u8,
|
||||||
|
data: []const u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const EmbedFn = *const fn (comptime path: []const u8) []const u8;
|
||||||
|
|
||||||
|
pub fn encodeFileBase64(
|
||||||
|
comptime embed_fn: EmbedFn,
|
||||||
|
comptime content_type: []const u8,
|
||||||
|
comptime path: []const u8,
|
||||||
|
) EncodedFile {
|
||||||
|
const raw = embed_fn(path);
|
||||||
|
const len = std.base64.standard.Encoder.calcSize(raw.len);
|
||||||
|
var buf: [len]u8 = undefined;
|
||||||
|
{
|
||||||
|
@setEvalBranchQuota(1_000_000);
|
||||||
|
_ = std.base64.standard.Encoder.encode(&buf, raw);
|
||||||
|
}
|
||||||
|
const final = buf;
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.content_type = content_type,
|
||||||
|
.data = &final,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn embedBase64Files(
|
||||||
|
comptime embed_fn: EmbedFn,
|
||||||
|
comptime content_type: []const u8,
|
||||||
|
comptime extension: []const u8,
|
||||||
|
comptime path: []const u8,
|
||||||
|
comptime names: []const []const u8,
|
||||||
|
) type {
|
||||||
|
var fields: [names.len]std.builtin.Type.StructField = undefined;
|
||||||
|
|
||||||
|
for (names, 0..) |name, i| {
|
||||||
|
const encoded = encodeFileBase64(
|
||||||
|
embed_fn,
|
||||||
|
content_type,
|
||||||
|
std.fmt.comptimePrint(
|
||||||
|
"{s}/{s}.{s}",
|
||||||
|
.{ path, name, extension },
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
var name_z: [name.len:0]u8 = undefined;
|
||||||
|
@memcpy(&name_z, name);
|
||||||
|
|
||||||
|
fields[i] = std.builtin.Type.StructField{
|
||||||
|
.name = &name_z,
|
||||||
|
.type = EncodedFile,
|
||||||
|
.is_comptime = true,
|
||||||
|
.alignment = 0,
|
||||||
|
.default_value_ptr = &encoded,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return @Type(.{ .@"struct" = .{
|
||||||
|
.layout = .auto,
|
||||||
|
.is_tuple = false,
|
||||||
|
.fields = &fields,
|
||||||
|
.decls = &[_]std.builtin.Type.Declaration{},
|
||||||
|
} });
|
||||||
|
}
|
|
@ -52,7 +52,7 @@ pub fn main() !void {
|
||||||
return clap.help(writer, clap.Help, ¶ms, .{});
|
return clap.help(writer, clap.Help, ¶ms, .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
const efa = try render.apps.Departures.efa.Client.init(allocator, res.args.efa.?);
|
const efa = try render.api.efa.Client.init(allocator, res.args.efa.?);
|
||||||
defer efa.deinit();
|
defer efa.deinit();
|
||||||
|
|
||||||
if (res.args.stopfinder) |query| {
|
if (res.args.stopfinder) |query| {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
pub const api = @import("api/root.zig");
|
||||||
pub const apps = @import("apps/root.zig");
|
pub const apps = @import("apps/root.zig");
|
||||||
pub const renderer = @import("renderer.zig");
|
pub const renderer = @import("renderer.zig");
|
||||||
pub const FeedClient = @import("FeedClient.zig");
|
pub const FeedClient = @import("FeedClient.zig");
|
||||||
|
|