From 16fd5b0b958016e0a3f49d31c839477cf1afa3df Mon Sep 17 00:00:00 2001 From: Dominic Grimm Date: Fri, 20 Jun 2025 15:13:43 +0200 Subject: [PATCH] Restructure render --- src/eink_feed_render/api/efa/models.zig | 90 ++++++++++++++++ .../Departures/efa.zig => api/efa/root.zig} | 97 +----------------- src/eink_feed_render/api/root.zig | 1 + src/eink_feed_render/apps/Departures/root.zig | 4 +- .../Departures/template/icons/img/boat.png | Bin 2129 -> 0 bytes .../Departures/template/icons/img/bus.png | Bin 1454 -> 0 bytes .../Departures/template/icons/img/flight.png | Bin 1119 -> 0 bytes .../Departures/template/icons/img/gondola.png | Bin 1568 -> 0 bytes .../template/icons/img/no_transfer.png | Bin 1501 -> 0 bytes .../Departures/template/icons/img/shuttle.png | Bin 1049 -> 0 bytes .../Departures/template/icons/img/subway.png | Bin 1812 -> 0 bytes .../Departures/template/icons/img/train.png | Bin 1493 -> 0 bytes .../Departures/template/icons/img/tram.png | Bin 1385 -> 0 bytes .../apps/Departures/template/icons/root.zig | 65 ++++++------ .../apps/Departures/template/root.zig | 13 ++- src/eink_feed_render/embed.zig | 67 ++++++++++++ src/eink_feed_render/main/departures.zig | 2 +- src/eink_feed_render/root.zig | 1 + 18 files changed, 207 insertions(+), 133 deletions(-) create mode 100644 src/eink_feed_render/api/efa/models.zig rename src/eink_feed_render/{apps/Departures/efa.zig => api/efa/root.zig} (61%) create mode 100644 src/eink_feed_render/api/root.zig delete mode 100644 src/eink_feed_render/apps/Departures/template/icons/img/boat.png delete mode 100644 src/eink_feed_render/apps/Departures/template/icons/img/bus.png delete mode 100644 src/eink_feed_render/apps/Departures/template/icons/img/flight.png delete mode 100644 src/eink_feed_render/apps/Departures/template/icons/img/gondola.png delete mode 100644 src/eink_feed_render/apps/Departures/template/icons/img/no_transfer.png delete mode 100644 src/eink_feed_render/apps/Departures/template/icons/img/shuttle.png delete mode 100644 src/eink_feed_render/apps/Departures/template/icons/img/subway.png delete mode 100644 src/eink_feed_render/apps/Departures/template/icons/img/train.png delete mode 100644 src/eink_feed_render/apps/Departures/template/icons/img/tram.png create mode 100644 src/eink_feed_render/embed.zig diff --git a/src/eink_feed_render/api/efa/models.zig b/src/eink_feed_render/api/efa/models.zig new file mode 100644 index 0000000..37c51e0 --- /dev/null +++ b/src/eink_feed_render/api/efa/models.zig @@ -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, +}; diff --git a/src/eink_feed_render/apps/Departures/efa.zig b/src/eink_feed_render/api/efa/root.zig similarity index 61% rename from src/eink_feed_render/apps/Departures/efa.zig rename to src/eink_feed_render/api/efa/root.zig index b184d70..0d02df0 100644 --- a/src/eink_feed_render/apps/Departures/efa.zig +++ b/src/eink_feed_render/api/efa/root.zig @@ -2,99 +2,10 @@ const std = @import("std"); const escaper = @import("../../escaper.zig"); +pub const models = @import("models.zig"); + 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 Error = error{ InvalidResponse, @@ -170,7 +81,7 @@ pub const Client = struct { 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); defer if (escaped_stop_id) |s| self.allocator.free(s); const url_stop_id = escaped_stop_id orelse stop_id; @@ -204,7 +115,7 @@ pub const Client = struct { defer self.allocator.free(body); const response = try std.json.parseFromSlice( - DmResponse, + models.DmResponse, self.allocator, body, .{ .allocate = .alloc_always, .ignore_unknown_fields = true }, diff --git a/src/eink_feed_render/api/root.zig b/src/eink_feed_render/api/root.zig new file mode 100644 index 0000000..8d90898 --- /dev/null +++ b/src/eink_feed_render/api/root.zig @@ -0,0 +1 @@ +pub const efa = @import("efa/root.zig"); diff --git a/src/eink_feed_render/apps/Departures/root.zig b/src/eink_feed_render/apps/Departures/root.zig index bb6f620..460cacc 100644 --- a/src/eink_feed_render/apps/Departures/root.zig +++ b/src/eink_feed_render/apps/Departures/root.zig @@ -5,7 +5,7 @@ const zdt = @import("zdt"); const Dimensions = @import("../../Dimensions.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"); @@ -28,7 +28,7 @@ pub const RenderOptions = struct { 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 }); var arena = std.heap.ArenaAllocator.init(self.allocator); diff --git a/src/eink_feed_render/apps/Departures/template/icons/img/boat.png b/src/eink_feed_render/apps/Departures/template/icons/img/boat.png deleted file mode 100644 index f49f410f4d89fa82a1ef197d33e95817db524dd6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2129 zcmV-X2(I^uP)J}JMiW9v4E_;~{!ugvk${R~1w{iGEh1=z@DvClB@vKUX}RV4cKv;{tC6So4Ei?Iet7}g*O!x|)ESc4=CYmkJ|HsbmQ0)2sQiq{V}pu=!rn&P!5 zfCIoT1A4?pM_?YXAN@W>RPrbAoYR(qW&p5*_&ZdW>~PvtkX#GY5`QP^mRhH61w}Ei zmH0c)lx%U@STM{W{!TM3k8A6#2-raU9cEg-*VbEEm z!*W``F+hB{GTnumFi2;GYbubpGs#k+(2i1tYkXltCkewEBw<*CBn)eigkcSmFrp5> z>!u7C4@_|Wy%t!9#G49|LNiDw;5Nhn6Mzw1?A&I;8T%09tOcroe@Y;J ztYTm!vYzq{a89A@=8^(70}Bu%wvVA5u@bf+{ehXlGT^vES$^d-V%&L%vGawGVlhY( zhOBa6Eb{bD2Cg<-UuT+zJb*4cL= z6N7!RX`~sISG~1}_94sqAqhj*ddPLaBw!+N2kL1)x;XFr*)HOGPXON`lY}Z@Z`cBZ z8l)}q9hia`p?_FKe3Ax!L&o}bzz@#P$0FaQ@xYBlKLYGSjPnKX70?((rTu_yL_c2d z0p13t13eY=yaM@9F9CMPpzl^9$WDneo8$=a5t1;WqhWm!Pkt$IIHETEis83(qCw?P z0-pnOfT5V*ZZ{Zs23Uz12Q(w&O*Y~wjf&O->w%Y%_*9%uYB6v#@B-o>o<(0fpD?NT zln{zF0>1zYk%fmlJS=Ft?=`7t7og6EGJBB^@id@^NyQA43AZdj z0+km~YyUY6BtkT2wSppZkdJ6PCgpI85_ zj6fI4@P0BrjzW=`c?0inW_eV~Z1Pa%Bv2BSGBM%hY;FuvXi%rcYuG9U#sw_95EzR0 z#vPTRy#+xHvO?jSsR7F_1XS7<$RJ_x+NySl1(o;IiQ|4gA} zjH=%Gc|N+}og4O+IhDPcoJXnVVU%2qB!AdDh1sB7C2%HB-F3(^LoTM}xhGpD8`RsA z5+qI2O~G$-+&2zck9NE73=$I8O+O^`;a2xKav#6(p~rovNPt893GPa!xH!@~74`Lo zqv*}s>V^8r5-H%xtb5y$dw9*3M1+hU#=CKkIM>cZGDK85BqImWlg-!_+3@eD?ldwB zYlE!(d0qRE20cU54Vj_1DY^phV4R!O09S_4u?O&Ti29e}J%MQ;`t+x~R1v(fs`3{w zEP!ski6%=XQAf|t!hD0%$QVA!hhD>o?pUZ{h0q#jVIGrsctxjYz^+(4^y1_<~;B|`T~^wfaj4gObThM zR!ppH^+uw(rcO_|9C>0VAv3#jB#H|X9X^75l&g^4RmTnK(G^J;|JzXc7K_DVQKtyF zK8AV+(0UX40#Ob;3;d4EjJ6BF?b*GVK;LX4v$QxFJ_&iz( z(m~lr8G$^xmt{#0=TFnrB4=Bjq{#{G>ePJ=(R>e1ArH#QEIFO^|H~awCdl|a1ohwH zWRf!C{|J}j9v|C|PlMRwnG%LINWw^(K~k4Uoq0)uK@I_LA`3m;k+VT+BB=K;@Cp*0 z9_;*mX9V?XXFlOp?HZB8#}*?-$n{VZA>s99$gD-zieVk_8Snu4BLSn4O+(d$TNFz> zedUWNPT)C;IAqnxajYBg_94>9dPWtp?{EUriDQu1lWJI{5jo{;6XN8pK^#6esvQBD zLXQD%N4D_YK)4)ZPjvVp^~i$MapVa+i)5_taYXk*RYzoq8AOxckx6nsL6cvy6Pb3N z)m(pPBnUbH@pn2oKg%pwWp7;d?T1(_7K_DVu~;k?OVs=a#1Z)dqmOpK00000NkvXX Hu0mjf|9R#C diff --git a/src/eink_feed_render/apps/Departures/template/icons/img/bus.png b/src/eink_feed_render/apps/Departures/template/icons/img/bus.png deleted file mode 100644 index 5598cff1a337975b4e893af7e5c4d01e9b1de5a2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1454 zcmV;f1yTBmP)K~#90?VVkSRaF$ne>yWd&RFB5hGP_H*_bu zowL`ytRHM<&fLBCT6_QRIcM*EX0HQ|<2a7vIF92uw)eFK4kGH@Hwy%cpo?v*ByO&fOWu+z(kX! zao`i+F68pq)XV~2K(6hvJ60q80<2K&p%QrjI7s|XEFygf%+~9wV(9>0BYvk9o%R6z zI$c&A9l*Q9@2s-YUSw3LhW04yW#9?rIwyb;WF9e!+>idM`mB;^oIY~vNsea%XC=p) zH|K4@b-+Iwm1zocCvt7AhY4U8@O_eY0e=9$1N)H)o)+!ExlB$x`+>`l`OpCM&3~Q+ zp3`zrgLdF&@}2I-fVY4Lkq)vz4>Cu36FD10+cM1LYJmw>67KvC;2~^I2rKCVo&*jM z)~?4ijA~u7-a$mk;tk*xqyf#sT;O%!yo`LCfLj|irXk1_WND#Sp{{1;IB+%%-tRO< z8w+qTKrX=-Hd={>Am=O8(JJ&cT7iZjrbZW$X}B*m1evZ-N2}1;Xa!BboZM=-AjWV( zj8VpOYk;duvQsqQ6>Xp_LAI4-r|I!amN8rqW4Iv3a6ydWf*8XEF@_6b3>U;0E{HK) z5Mz|Jbh4@>J55hnf@~=PHfk%L4$S&DKz!qe$`%pnP=Oi|7LIMCB1-?ma?k>n?X{;i9 z|6Lkufw7b_Bf#AS_`4#hyOzx{V9goIX0_AAs&>7CtPLo461Yv#*X2o_S$3aFDc`b! zEFk^Ns80LP*|FQ5)D>cLjN04N3Kt~SP5n_M;z9_0$lmcrSxNYovuVJ?3N~{SOOR`F z=D)T{ZRRGHAbI!V%~G(Lw+;=Pxrrsn!JPRIDA*i~U^6#m3DQ<|yd`J(y!mUfxj~!N zZ7@s7C~IC-nK`7-1*+3_bULFA_$kolFmO&)`M!X1CPrDO0`e@WIvxU62b3KJ?hmw) z#VO#vq|U5-tAWE+f4?-)Ms#i@`EXlXO#%Eo?y~tYD>?F@-;A4vW#Wh<)I~_}wWxF@la-2eap diff --git a/src/eink_feed_render/apps/Departures/template/icons/img/flight.png b/src/eink_feed_render/apps/Departures/template/icons/img/flight.png deleted file mode 100644 index f6416af3654b8edcb13293d5dad0eafc79927bde..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1119 zcmeAS@N?(olHy`uVBq!ia0vp^DImL}7?-@9I+Yr!J7ZUv66uCA3X7Y;>aK3%-!%B4e2j;!8WH*;oWCMt6* zx{Qf&it{Qy}3C3SyQ4! z8yLAorFpOiG~YbHm!Q63(~o=E5A+J;XNYvJTKb0lSmXW688SE2#bUc>FwRM@Qno6c zx92_6E6d*fl1u%%Ty8OKW7@wW?}UFsx5J*^=WJKcYLvz*+;iJ3@WbzYTMe_AO8+kQ zG%xlCC)pDGdhgHYUc)cq9a3bNlG$K3jWt8rYNteyy0z0b!MQO!nY?KoMrQ<4XDFUB zaGsLbrg4}juuXCikFkp+4v8Zx&bTG?+!ME$QpLuj+P63G3`<{7{#MWBYK_JY@w@U& znjIg=J?wdL#W}%u)ww6<7#}cyJfrbd(qhgE|Hw@oJDh5O{^5<7w4grHu;=#eiMM)! z@*DSFte(mE_dw*GrVLGEufI%sOrIBji!gNF5fpN(!>^b5w$is~+1dkzho)^_Xxe%D z&drI(9C(|L=%zcbzWDT`V$4cS+Y_OtjnxjhJl+LMmwq{L?W%4<<%5l$=L|lu{XEFC zH0Si=x(7^q-*TRDoPKhJ&9$g=ZPR{zvS=?d+j3>v@#ZI?K>n&v7T1CNBc~rPo3eSK zYLMLfWsy&FGE}wuXUCmu3%l~Ef}>*Hg2_8ID=P1>&ta ztVlU~<1XtXKeXQci9S5vDEr)lXP4Mq7IS*6IO+N|W9I2s$J|tNx6He+dsWW%5Z zt4@_!t`Pa$v48cdue+DlaF%*Btbb$BnAuSXVeqkC+{hDpwJ1O{NLJ{<>T7ap3Lqs> z;d1s)br*KO4BT?>Kl@7cuQI26CrT90v;wEenq~L5eiqshx#u498nGVL=y{iZ+uxpl zAoxJ()~fk`L&W)ctxU6O(g% j39}*3VFNJoI4E!9^xj@famyKCiNWCM>gTe~DWM4fxQhhi diff --git a/src/eink_feed_render/apps/Departures/template/icons/img/gondola.png b/src/eink_feed_render/apps/Departures/template/icons/img/gondola.png deleted file mode 100644 index ead5e99f603ffe53b0fd3f3e88b5e7be30baf4e3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1568 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 DE>N~5 diff --git a/src/eink_feed_render/apps/Departures/template/icons/img/shuttle.png b/src/eink_feed_render/apps/Departures/template/icons/img/shuttle.png deleted file mode 100644 index bc9b2e9bb975566193622249023d82250b273686..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1049 zcmV+!1m^pRP)rKFOD~Gd<^g?|JW> znfc~+KnNj(5JCtcgb+dqA%qY@2qA>~Um#1;*Q`E+77lkJmM&~e-!k&dwexJ9~=V>yPYS{%xkT6;Rm>Zm5X12zJ$bF>+t#N|td zj3=lmG9;N`rpS=?1TjU1q*J*mG9;TyO_3pXRA!0{v8NJKWJo@|n<7K<DgA(0_NB148mMiE1fW4;EX$d2QACs#*EWXO=nkRg##q_~(x7qA6b4=e%}Hn)Go zR7&pvH-MiW#x9d+bYvA58*SQk0{el_8SO`bPryF>d>~vYQ%57C{h&=kMGRRBJhl+f z8jpZAHc3@P**Y2-?Zz2W#yMm&=IfWM3bb7f+y%ClkcTT#=qR#e^kW|GfO$Bx4VdPT z!z|z`a1Hq5(2ongbganRNz%VX4;JZ26yh1sH+F+Adn3c=Rn9PQ9eCROeHCVno#Amz z9|YG?#xwQ%m}~b|7B}~Wey$yhj#4grv*I!41lNw)m{(SWp6nQORAR^`i!nAkj!{Ka zp~Jf&U$DDS=m5Stit!m$=@@57(nRnoQft{EWXJ7N@dlv0Z zNk@LVfo~Q`<&E#a0_iBm;Sv&WjnZCGk%PMEpaWC?FH6K#!c?E@q62_Al=C{L2bkJW zDC(jEFlFd-6c=$Bm|1nw3DiZ$e|7+`uov(W*d8R)bn2o5faw@PmoRt2t;r7o7cl}{ zo?rVWn2x-!8qZ4?=GEk2zUMUTc|2d#MTZbV2qAw;W{`hC0~@AvcZc>MwISEjS00}QGR1poks#n`#* ziT}BhkiAQsz3;XMB=DSr9kBazD%z^@_kv!moweKblI7Bf40m@p*|NABKHx!vn%i8G z4?LsuT4VXbW@WnPm}o~{q{+9ocZ1s>`Nb>j*zt7-YcCgT`(Uk5?!C;{hw)fP>Y;^D zfkg=^!O?QKmEt2bzg0T&?kn$y|IFsQtHZt_b&YC^OO0Dr>T;4|;N+i-29qQ91)3@U zvX6qYL!);r3P6(Bpx_bnWWEy#NJOp)Q}LlT(*EM!f1gN z z&50`bilRh6Q1Bt-0!O196r#9*JXf+mA?KDv!I9sSjiDR3OlinL;44Yk#QF#{r2OEP zUF%1q^_hrZ?@P&E;@h%1QKPq%R^ddv^-k@WY?A};Mbbw0JN~Y^X{@uTHLqM+0&VB= zuf#2I)`s3|m0S?JkzH!ODFRy}I|t%0QL9P~ioJ^QAK&@~=;VE%i03%Ol=ZSr@bEY{ z$L~Yx$%4KbZR2Cqw%T2Azrk_Dz=G$VLmSezp4L({KUA*KP$fO-mac zx7}Gx7;SlS6;tRP3)};G#!oZ>Xw8#;+B=JkFm4NETy z3zp5J9JSwhwswf%_gH7KkMb01`{-y3F_d$y0EYn%ja)K+y!>dOShFHd$7&`>TfK!#+A% zUDhl=whqU?Xe1dHoewpR`>bARLXi28|Bd`yJSdQ6Jru>L?;ns(FZOnY$OY8xG42*dBZ1OsKbUm~0>&;>~@rQ#c-2i#8Tb1#M56Eg38t zP{L=!XZ6|2=T3y07mkCtO4Fo3y$wxn>3Lr*kZdGOKw+L~g!n zp%6juU8l&&4M~F5enOn8@faol12&QKJnhZ-FT$8+P&BysA?uUN>cb+Ir{lWRt3E|k z-e3}~Gnx{>KHGWY#+>WU$PrwqUD^zGp2k_$AGt8>!3nQj=qfDbASQ4&Gy-RRWw2Jb zW#5wEV?ztSEP>Z$!R!rCx&a^c)?4s#hI*=iuL1qMX$$zkyvR~|p7z_&%Wg_=w~sA- z6pESjo(cL-b{jiC`apOMPQVyMisQZex0>J4>;B3RY1$i~dQHFNm4zr+Mzk6m?Q4x%3n_;GtSQN-n zpIhclZ97|_(YMx(Y~IXExr)0V>ZEsf;?WHlDG7;iRXdIS?q1cRI8zGPwK!G&3F zuM)e90|{*B-CdfANW_0!@WM&4;=f~K`)Uc?98sJrRib>DWUj`8P0Cv2;D;KMrFz>N zy>TayrU9_kD14!D!h?zhh_CJEIn)qw35qYqE&FEn0BV{lov%p1bxO=(e1VO8COuP& z;be3{FL+GzH))n2IBzAJy}y-dRbBkbFjEWvNAs`gkHwf3yH0KlI!sWJG#GYA@v;PC z$=|KIxZ@$^VZc q3&x{Z{U4TMevtq6h!_|o+*X>kHA%VCF=(_`a{$)f(T-{3fBSFpFj&q2 diff --git a/src/eink_feed_render/apps/Departures/template/icons/img/train.png b/src/eink_feed_render/apps/Departures/template/icons/img/train.png deleted file mode 100644 index d482e8d59e2a3404d5249fd7f8e9539cb52ca893..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1493 zcmV;`1uFW9P)TK~#90?VVkWT~idtf7*$m133~CEx@6 z2dk)Z2w1KXxd`pP@O5B=a+}rpL@T@RPFLFO=K+A4LhWL%BVdH$`><-znS89qY0J!vqNo6C^N9kialO z0>cCe3=cCe z3=_IlEu0h^d9|JxIUZMrx<(f?&$@}l>F%Q^EeA#*qj2k z5Z`koou~Gyb|$h4ven_Z8K7U$*V`R!ZizX*Z3XGYJt0jQg^e)C#-fU&%}MH~Wy)O; zUpMuE6tlJKnz+Kq+$ky$`@g+#wx8W~Su(>j2{mT_>rpA{bi&ECt*yWqnX4?8H z_!8t$%K8TtY?f2jPwS%!z62RcS^pCSn`zt7u<3;?qpTiep5Zd$*@L8pZI%VVFOD|H zk!(S;X^BzRbOt+nfd?Fpn*i?MXnw3_ID*VWCLHSC5BxUkcP_DViACD%M3j5Rs%ve-qm3f$^GjX@%?ki^y4-n+@xVl>sj|QW!);(uU5wnFr&DR!j@uZ&9m{hH@l&S<$u#OaI7OFHlJhZoV;$|2 zc;BT=BRN%q=5hQ7s;KgqQp|GaA>>BqJ79TU8Jo3&RTMWOGhpAr(=KIX*4~nw2s}c% zwB4bWG2jW>ByGko}PZ6qF9v*GteHz}3K8BkjM=zeDY;YBhMgx9J|6dGRf_{4=M3Rx;>yAT9H zP^hn!RuNl71YPK^y3vIQMWef_rD{Q}_`oXSLRw6#gK z!s*HsPL1U;L6l*FD8mF%Myi|o^T2?oeGLQ8FP4FGF^i-8AozV{_?C(>K`r^)7mMe2c6 zlZ#8~0Y;Jetqs6PlU?H3*9qWPU^25K+2`+p3D3R?lS+_b;11vh;6~tDM3mGV2Mz$= z0^b1pfcc~m1!NwqVbwT0BlEo-NN-N5Z$GZcxH zz(c?c=@S{kK0vzNm0$q)jPywhZO4G?{iG}$7XUwzK3TDB7TDw^a{*`|H^9_Mift3f z7|9Dh2Zj$L1F=r|890oL4W@wKkb&GRaxa?6em>Roos&pUsF^;Xx9M|l_PYUKb!KaU zLEtiEoHK%Sy?ZInmB6#WQyqLa82!k4Ly;rv6!1Q9ACjEZ^Z+*_Yc;=9tS$4%?138Y z$36II;3eef)Jg-m3-}oKc0H0L(q-6vR1ktj{rGe79uqPLlVS*?l)mNCkQQul@V999c=m0NlD1%bfo*pLiy{zJ z6J$Nb-l}j7QY@`9R85dhl52tlWTAuaRH_SHH9-zitg7fdQq-n0R85e7fp-dQzYD1t z)K$dp_6{PYoAP{4;lDypCAz({4%?Le|LrFJ1jdlUn#*xG+vb=@zvnq@|XKk>AeI0DcQ|DfxlEkSscXyhwgKeOKY`m`U0PI)hXV zsw!6?m!o!VoJ)+5n~YdUg+1rE%@)^m*Q63r^V}LYU{!0 zz4fM!w$n@I0?|O$v{NT3mVJ(n_nx$69k8GD$qH@zsZhz=9^g&VCoPn{K?Tl?R@{qB znWs*mFFT0;b^7QufCqq&kOijHNiAv%z=v5If9BKLz}OydZ4p8yIRw5AOBi$MJJyt^(e6^tl%}*MrYWkV1?_Oq3<06k`z+ zC8LNK?-KR~+V!Gn5@V6B7G)1^4c~<%Di*6UpDwZOi5ORKG@ThsDRQO+CsqQ!bNKsK r train, + => data.train, - .u_bahn => subway, + .u_bahn => data.subway, - .stadtbahn, .strassen_trambahn => tram, + .stadtbahn, .strassen_trambahn => data.tram, .stadtbus, .regionalbus, @@ -24,40 +47,18 @@ pub fn iconFromTransportationProductClass(class: efa.ApiTransportationProductCla .rufbus_liniengebunden, .rufbus, .anruf_sammel_taxi, - => bus, + => data.bus, - .seil_zahnradbahn => gondola, + .seil_zahnradbahn => data.gondola, - .schiff => boat, + .schiff => data.boat, .sonstige => null, - .flugzeug => flight, + .flugzeug => data.flight, - .sev => no_transfer, + .sev => data.no_transfer, _ => 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"); diff --git a/src/eink_feed_render/apps/Departures/template/root.zig b/src/eink_feed_render/apps/Departures/template/root.zig index 862dc59..382bcfb 100644 --- a/src/eink_feed_render/apps/Departures/template/root.zig +++ b/src/eink_feed_render/apps/Departures/template/root.zig @@ -1,7 +1,7 @@ const std = @import("std"); const escaper = @import("../../../escaper.zig"); -const efa = @import("../efa.zig"); +const api = @import("../../../api/root.zig"); const icons = @import("icons/root.zig"); @@ -9,7 +9,7 @@ pub const Departure = struct { time: []const u8, time_unix: i128, line: []const u8, - transportation_product_class: efa.ApiTransportationProductClass, + transportation_product_class: api.efa.models.TransportationProductClass, operator: ?[]const u8, destination: []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( \\ - \\ + \\ \\ , - .{ .img = img }, + .{ + .content_type = file.content_type, + .data = file.data, + }, ); } else { try writer.writeAll( diff --git a/src/eink_feed_render/embed.zig b/src/eink_feed_render/embed.zig new file mode 100644 index 0000000..2b6b983 --- /dev/null +++ b/src/eink_feed_render/embed.zig @@ -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{}, + } }); +} diff --git a/src/eink_feed_render/main/departures.zig b/src/eink_feed_render/main/departures.zig index 38bc120..53a6cee 100644 --- a/src/eink_feed_render/main/departures.zig +++ b/src/eink_feed_render/main/departures.zig @@ -52,7 +52,7 @@ pub fn main() !void { 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(); if (res.args.stopfinder) |query| { diff --git a/src/eink_feed_render/root.zig b/src/eink_feed_render/root.zig index 7fa8232..f7b04eb 100644 --- a/src/eink_feed_render/root.zig +++ b/src/eink_feed_render/root.zig @@ -1,3 +1,4 @@ +pub const api = @import("api/root.zig"); pub const apps = @import("apps/root.zig"); pub const renderer = @import("renderer.zig"); pub const FeedClient = @import("FeedClient.zig");