lib/zen/src/diagram/ascii/render.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2
3 const root = @import("../root.zig");
4 const canvas_mod = @import("canvas.zig");
5 const mark = @import("mark.zig");
6 const model = @import("model.zig");
7 const plan_mod = @import("plan.zig");
8 const plot = @import("plot.zig");
9 const storage_mod = @import("storage.zig");
10
11 const bounds = root.bounds;
12 const spec = root.spec;
13 const Canvas = canvas_mod.Canvas;
14 const Options = model.Options;
15 const RenderPlan = plan_mod.Plan;
16 const RenderStorage = storage_mod.Storage;
17 const RenderStatus = storage_mod.Status;
18 const RenderError = model.Error;
19
20 /// Owns one ASCII result and its bounded backing region.
21 pub const Rendered = struct {
22 storage: RenderStorage,
23 output: []u8,
24
25 /// Releases the result with the allocator passed to rendering.
26 pub fn deinit(self: *Rendered, allocator: std.mem.Allocator) void {
27 self.storage.reset();
28 self.storage.deinit(allocator);
29 self.* = undefined;
30 }
31
32 /// Reports final, high-water, and rejected render demand.
33 pub fn status(self: *const Rendered) RenderStatus {
34 return self.storage.status();
35 }
36 };
37
38 /// Parses a JSONL diagram and returns an owned ASCII result.
39 pub fn renderJsonl(
40 allocator: std.mem.Allocator,
41 jsonl: []const u8,
42 options: Options,
43 ) !Rendered {
44 var document_storage = try root.DocumentStorage.initExact(allocator, jsonl);
45 defer document_storage.deinit(allocator);
46 document_storage.activate();
47 const document = try root.parse(&document_storage, jsonl);
48 defer document_storage.reset();
49 return try renderOwned(allocator, document, options);
50 }
51
52 /// Returns an owned ASCII result from a borrowed diagram document.
53 pub fn renderOwned(
54 allocator: std.mem.Allocator,
55 document: *const spec.Document,
56 options: Options,
57 ) !Rendered {
58 const plan = try RenderPlan.inspect(document, options);
59 var storage = try RenderStorage.init(allocator, plan.exactLimits());
60 errdefer storage.deinit(allocator);
61 storage.activate();
62 const output = try render(&storage, document, options);
63 return .{
64 .storage = storage,
65 .output = output,
66 };
67 }
68
69 /// Renders into active storage; returned bytes borrow it until `reset`.
70 pub fn render(
71 storage: *RenderStorage,
72 document: *const spec.Document,
73 options: Options,
74 ) RenderError![]u8 {
75 const plan = try RenderPlan.inspect(document, options);
76 const regions = try storage.acquire(plan);
77 errdefer storage.reset();
78 var canvas = Canvas.init(regions.canvas, plan.width, plan.height);
79 const plot_bounds = try bounds.computeIn(
80 regions.categories,
81 regions.stack_totals,
82 document,
83 );
84 const plot_area = plot.area(plan.width, plan.height, document.frame);
85
86 if (document.frame.title.len > 0) {
87 canvas.writeCentered(plan.width / 2, 0, document.frame.title);
88 }
89 if (document.frame.axes) {
90 try plot.drawAxes(&canvas, plot_area, document, plot_bounds);
91 }
92 try mark.drawAll(&canvas, plot_area, plot_bounds, document);
93 if (document.frame.axes) {
94 plot.drawCategoryLabels(&canvas, plot_area, plot_bounds);
95 }
96 return canvas.writeOutput(regions.output);
97 }