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 }