lib/closure/src/receipt/rows.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const std = @import("std");
  2 const pretty_json = @import("pretty").json;
  3 const closure = @import("../root.zig");
  4 const model = @import("model.zig");
  5 const schema = @import("../schema/root.zig");
  6 
  7 const limits = closure.limits;
  8 
  9 const JsonWriter = pretty_json.Writer;
 10 
 11 pub fn emit(
 12     writer: *std.Io.Writer,
 13     input: schema.GenerationInput,
 14     selected: *const schema.Claim,
 15     context: model.Context,
 16 ) std.Io.Writer.Error!void {
 17     try generationRow(writer, input, selected, context);
 18     try graphRows(writer, input);
 19     try artifactRows(writer, input);
 20     try evidenceRows(writer, input);
 21     try policyRows(writer, input);
 22     try claimRows(writer, input, selected.id, context);
 23 }
 24 
 25 fn graphRows(writer: *std.Io.Writer, input: schema.GenerationInput) std.Io.Writer.Error!void {
 26     for (input.nodes) |*node| try nodeRow(writer, input.id, node);
 27     for (input.edges) |*edge| try edgeRow(writer, input.id, edge);
 28 }
 29 
 30 fn artifactRows(writer: *std.Io.Writer, input: schema.GenerationInput) std.Io.Writer.Error!void {
 31     for (input.artifacts) |*artifact| {
 32         try artifactRow(writer, input.id, artifact);
 33     }
 34     for (input.ranges) |*range| try rangeRow(writer, input.id, range);
 35     for (input.digests) |*digest| try digestRow(writer, input.id, digest);
 36 }
 37 
 38 fn evidenceRows(writer: *std.Io.Writer, input: schema.GenerationInput) std.Io.Writer.Error!void {
 39     for (input.provenance_parents) |*parent| {
 40         try provenanceRow(writer, input.id, parent);
 41     }
 42     for (input.source_records) |*source| {
 43         try sourceRow(writer, input.id, source);
 44     }
 45     for (input.build_records) |*build| {
 46         try buildRow(writer, input.id, build);
 47     }
 48 }
 49 
 50 fn policyRows(writer: *std.Io.Writer, input: schema.GenerationInput) std.Io.Writer.Error!void {
 51     for (input.authorities) |*authority| {
 52         try authorityRow(writer, input.id, authority);
 53     }
 54     for (input.lineage_references) |*lineage| {
 55         try lineageRow(writer, input.id, lineage);
 56     }
 57     for (input.service_descriptors) |*service| {
 58         try serviceRow(writer, input.id, service);
 59     }
 60     for (input.residual_roots) |*root| {
 61         try residualRootRow(writer, input.id, root);
 62     }
 63 }
 64 
 65 fn claimRows(
 66     writer: *std.Io.Writer,
 67     input: schema.GenerationInput,
 68     selected: schema.ClaimId,
 69     context: model.Context,
 70 ) std.Io.Writer.Error!void {
 71     for (input.claims) |*claim| {
 72         try claimRow(writer, input.id, claim, selected, context);
 73     }
 74     for (input.claim_nodes) |*node| {
 75         try claimNodeRow(writer, input.id, node);
 76     }
 77 }
 78 
 79 fn generationRow(
 80     writer: *std.Io.Writer,
 81     input: schema.GenerationInput,
 82     selected: *const schema.Claim,
 83     context: model.Context,
 84 ) std.Io.Writer.Error!void {
 85     var stream = JsonWriter.init(writer, .minified);
 86     const sink = &stream;
 87     try rowStart(sink, "generation", input.id);
 88     try numberField(sink, "selected_claim", selected.id);
 89     try digestField(sink, "root", &input.root);
 90     try digestField(sink, "before_root", &context.before_root);
 91     try digestField(sink, "after_root", &context.after_root);
 92     try enumField(sink, "verdict", context.verdict);
 93     try optionalStringField(sink, "reason", context.reason);
 94     try optionalStringField(sink, "rejection", context.rejection);
 95     try optionalStringField(sink, "unknown", context.unknown);
 96     try objectField(sink, "limits");
 97     try writeCounts(sink, model.Counts.fromLimits(context.limits));
 98     try objectField(sink, "capacity");
 99     try writeCapacity(sink, context.capacity);
100     try objectField(sink, "occupancy");
101     try writeCounts(sink, context.occupancy);
102     try objectField(sink, "high_water");
103     try writeCounts(sink, context.high_water);
104     try numberField(sink, "work", context.work);
105     try numberField(sink, "visited_high_water", context.visited_high_water);
106     try numberField(sink, "queue_high_water", context.queue_high_water);
107     try numberField(sink, "allocation_count", 0);
108     try objectField(sink, "profiles");
109     try writeProfiles(sink, &selected.profiles);
110     try rowEnd(sink);
111 }
112 
113 fn nodeRow(
114     writer: *std.Io.Writer,
115     generation: schema.GenerationId,
116     node: *const schema.Node,
117 ) std.Io.Writer.Error!void {
118     var stream = JsonWriter.init(writer, .minified);
119     const sink = &stream;
120     try rowStart(sink, "node", generation);
121     try numberField(sink, "id", node.id);
122     try stringField(sink, "descriptor", node.descriptor.slice());
123     try digestField(sink, "identity", &node.identity);
124     try enumField(sink, "subject_kind", node.subject_kind);
125     try enumField(sink, "material_role", node.material_role);
126     try enumField(sink, "origin", node.origin);
127     try numberField(sink, "phase_bits", node.phases);
128     try phaseField(sink, node.phases);
129     try enumField(sink, "execution_locus", node.execution_locus);
130     try stringField(sink, "owner", node.owner.slice());
131     try enumField(sink, "authority", node.authority);
132     try enumField(sink, "artifact_kind", node.artifact_kind);
133     try rowEnd(sink);
134 }
135 
136 fn edgeRow(
137     writer: *std.Io.Writer,
138     generation: schema.GenerationId,
139     edge: *const schema.Edge,
140 ) std.Io.Writer.Error!void {
141     var stream = JsonWriter.init(writer, .minified);
142     const sink = &stream;
143     try rowStart(sink, "edge", generation);
144     try numberField(sink, "id", edge.id);
145     try numberField(sink, "source", edge.source);
146     try numberField(sink, "target", edge.target);
147     try enumField(sink, "kind", edge.kind);
148     try rowEnd(sink);
149 }
150 
151 fn artifactRow(
152     writer: *std.Io.Writer,
153     generation: schema.GenerationId,
154     artifact: *const schema.Artifact,
155 ) std.Io.Writer.Error!void {
156     var stream = JsonWriter.init(writer, .minified);
157     const sink = &stream;
158     try rowStart(sink, "artifact", generation);
159     try numberField(sink, "id", artifact.id);
160     try numberField(sink, "node", artifact.node);
161     try numberField(sink, "byte_length", artifact.byte_length);
162     try digestField(sink, "digest", &artifact.digest);
163     try enumField(sink, "witness", artifact.witness);
164     try rowEnd(sink);
165 }
166 
167 fn rangeRow(
168     writer: *std.Io.Writer,
169     generation: schema.GenerationId,
170     range: *const schema.ByteRange,
171 ) std.Io.Writer.Error!void {
172     var stream = JsonWriter.init(writer, .minified);
173     const sink = &stream;
174     try rowStart(sink, "range", generation);
175     try numberField(sink, "id", range.id);
176     try numberField(sink, "artifact", range.artifact);
177     try numberField(sink, "offset", range.offset);
178     try numberField(sink, "length", range.length);
179     try digestField(sink, "digest", &range.digest);
180     try booleanField(sink, "executable", range.executable);
181     try enumField(sink, "witness", range.witness);
182     try rowEnd(sink);
183 }
184 
185 fn digestRow(
186     writer: *std.Io.Writer,
187     generation: schema.GenerationId,
188     digest: *const schema.DigestRecord,
189 ) std.Io.Writer.Error!void {
190     var stream = JsonWriter.init(writer, .minified);
191     const sink = &stream;
192     try rowStart(sink, "digest", generation);
193     try numberField(sink, "id", digest.id);
194     try numberField(sink, "node", digest.node);
195     try stringField(sink, "purpose", digest.purpose.slice());
196     try digestField(sink, "digest", &digest.digest);
197     try enumField(sink, "witness", digest.witness);
198     try rowEnd(sink);
199 }
200 
201 fn provenanceRow(
202     writer: *std.Io.Writer,
203     generation: schema.GenerationId,
204     parent: *const schema.ProvenanceParent,
205 ) std.Io.Writer.Error!void {
206     var stream = JsonWriter.init(writer, .minified);
207     const sink = &stream;
208     try rowStart(sink, "provenance", generation);
209     try numberField(sink, "id", parent.id);
210     try numberField(sink, "child", parent.child);
211     try numberField(sink, "parent", parent.parent);
212     try rowEnd(sink);
213 }
214 
215 fn sourceRow(
216     writer: *std.Io.Writer,
217     generation: schema.GenerationId,
218     source: *const schema.SourceRecord,
219 ) std.Io.Writer.Error!void {
220     var stream = JsonWriter.init(writer, .minified);
221     const sink = &stream;
222     try rowStart(sink, "source", generation);
223     try numberField(sink, "id", source.id);
224     try numberField(sink, "node", source.node);
225     try stringField(sink, "path", source.path.slice());
226     try digestField(sink, "digest", &source.digest);
227     try enumField(sink, "witness", source.witness);
228     try rowEnd(sink);
229 }
230 
231 fn buildRow(
232     writer: *std.Io.Writer,
233     generation: schema.GenerationId,
234     build: *const schema.BuildRecord,
235 ) std.Io.Writer.Error!void {
236     var stream = JsonWriter.init(writer, .minified);
237     const sink = &stream;
238     try rowStart(sink, "build", generation);
239     try numberField(sink, "id", build.id);
240     try numberField(sink, "node", build.node);
241     try numberField(sink, "tool", build.tool);
242     try stringField(sink, "option", build.option.slice());
243     try digestField(sink, "digest", &build.digest);
244     try enumField(sink, "witness", build.witness);
245     try rowEnd(sink);
246 }
247 
248 fn authorityRow(
249     writer: *std.Io.Writer,
250     generation: schema.GenerationId,
251     authority: *const schema.Authority,
252 ) std.Io.Writer.Error!void {
253     var stream = JsonWriter.init(writer, .minified);
254     const sink = &stream;
255     try rowStart(sink, "authority", generation);
256     try numberField(sink, "id", authority.id);
257     try numberField(sink, "source", authority.source);
258     try numberField(sink, "target", authority.target);
259     try enumField(sink, "granted", authority.granted);
260     try rowEnd(sink);
261 }
262 
263 fn lineageRow(
264     writer: *std.Io.Writer,
265     generation: schema.GenerationId,
266     lineage: *const schema.LineageReference,
267 ) std.Io.Writer.Error!void {
268     var stream = JsonWriter.init(writer, .minified);
269     const sink = &stream;
270     try rowStart(sink, "lineage", generation);
271     try numberField(sink, "id", lineage.id);
272     try numberField(sink, "node", lineage.node);
273     try stringField(sink, "descriptor", lineage.descriptor.slice());
274     try digestField(sink, "digest", &lineage.digest);
275     try enumField(sink, "witness", lineage.witness);
276     try rowEnd(sink);
277 }
278 
279 fn serviceRow(
280     writer: *std.Io.Writer,
281     generation: schema.GenerationId,
282     service: *const schema.ServiceDescriptor,
283 ) std.Io.Writer.Error!void {
284     var stream = JsonWriter.init(writer, .minified);
285     const sink = &stream;
286     try rowStart(sink, "service", generation);
287     try numberField(sink, "id", service.id);
288     try numberField(sink, "node", service.node);
289     try stringField(sink, "provider", service.provider.slice());
290     try stringField(sink, "protocol", service.protocol.slice());
291     try stringField(sink, "endpoint_rule", service.endpoint_rule.slice());
292     try stringField(sink, "trust_anchor", service.trust_anchor.slice());
293     try stringField(sink, "failure_contract", service.failure_contract.slice());
294     try enumField(sink, "requirement", service.requirement);
295     try rowEnd(sink);
296 }
297 
298 fn residualRootRow(
299     writer: *std.Io.Writer,
300     generation: schema.GenerationId,
301     root: *const schema.ResidualRoot,
302 ) std.Io.Writer.Error!void {
303     var stream = JsonWriter.init(writer, .minified);
304     const sink = &stream;
305     try rowStart(sink, "root", generation);
306     try numberField(sink, "id", root.id);
307     try numberField(sink, "claim", root.claim);
308     try numberField(sink, "node", root.node);
309     try enumField(sink, "treatment", root.treatment);
310     try rowEnd(sink);
311 }
312 
313 fn claimRow(
314     writer: *std.Io.Writer,
315     generation: schema.GenerationId,
316     claim: *const schema.Claim,
317     selected_id: schema.ClaimId,
318     context: model.Context,
319 ) std.Io.Writer.Error!void {
320     var stream = JsonWriter.init(writer, .minified);
321     const sink = &stream;
322     const selected = claim.id == selected_id;
323     try rowStart(sink, "claim", generation);
324     try numberField(sink, "id", claim.id);
325     try stringField(sink, "name", claim.name.slice());
326     try numberField(sink, "artifact", claim.artifact);
327     try digestField(sink, "artifact_digest", &claim.artifact_digest);
328     try objectField(sink, "profiles");
329     try writeProfiles(sink, &claim.profiles);
330     try numberField(sink, "traversed_edge_bits", claim.traversed_edges);
331     try edgeSetField(sink, claim.traversed_edges);
332     try booleanField(sink, "selected", selected);
333     try enumField(
334         sink,
335         "verdict",
336         if (selected) context.verdict else schema.Verdict.not_exercised,
337     );
338     try optionalStringField(sink, "reason", if (selected) context.reason else null);
339     try optionalStringField(sink, "rejection", if (selected) context.rejection else null);
340     try optionalStringField(sink, "unknown", if (selected) context.unknown else null);
341     try rowEnd(sink);
342 }
343 
344 fn claimNodeRow(
345     writer: *std.Io.Writer,
346     generation: schema.GenerationId,
347     node: *const schema.ClaimNode,
348 ) std.Io.Writer.Error!void {
349     var stream = JsonWriter.init(writer, .minified);
350     const sink = &stream;
351     try rowStart(sink, "claim-node", generation);
352     try numberField(sink, "id", node.id);
353     try numberField(sink, "claim", node.claim);
354     try numberField(sink, "node", node.node);
355     try enumField(sink, "treatment", node.treatment);
356     try rowEnd(sink);
357 }
358 
359 fn rowStart(
360     sink: *JsonWriter,
361     family: []const u8,
362     generation: schema.GenerationId,
363 ) std.Io.Writer.Error!void {
364     var schema_buffer: [64]u8 = undefined;
365     const schema_name = std.fmt.bufPrint(
366         &schema_buffer,
367         "tiny.closure.{s}/v1",
368         .{family},
369     ) catch unreachable;
370     try sink.beginObject();
371     try sink.objectField("schema");
372     try sink.byteString(schema_name);
373     try sink.objectField("vocabulary");
374     try sink.byteString("ClosureManifest/v1");
375     try sink.objectField("generation");
376     try sink.write(generation);
377 }
378 
379 fn rowEnd(sink: *JsonWriter) std.Io.Writer.Error!void {
380     try sink.endObject();
381     try sink.newline();
382 }
383 
384 fn fieldStart(sink: *JsonWriter, name: []const u8) std.Io.Writer.Error!void {
385     try sink.objectField(name);
386 }
387 
388 fn numberField(sink: *JsonWriter, name: []const u8, value: anytype) std.Io.Writer.Error!void {
389     try fieldStart(sink, name);
390     try sink.write(value);
391 }
392 
393 fn booleanField(sink: *JsonWriter, name: []const u8, value: bool) std.Io.Writer.Error!void {
394     try fieldStart(sink, name);
395     try sink.write(value);
396 }
397 
398 fn stringField(
399     sink: *JsonWriter,
400     name: []const u8,
401     value: []const u8,
402 ) std.Io.Writer.Error!void {
403     try fieldStart(sink, name);
404     try sink.byteString(value);
405 }
406 
407 fn optionalStringField(
408     sink: *JsonWriter,
409     name: []const u8,
410     value: ?[]const u8,
411 ) std.Io.Writer.Error!void {
412     try fieldStart(sink, name);
413     if (value) |present| {
414         try sink.byteString(present);
415     } else {
416         try sink.write(null);
417     }
418 }
419 
420 fn enumField(sink: *JsonWriter, name: []const u8, value: anytype) std.Io.Writer.Error!void {
421     try stringField(sink, name, @tagName(value));
422 }
423 
424 fn digestField(
425     sink: *JsonWriter,
426     name: []const u8,
427     digest: *const schema.Digest,
428 ) std.Io.Writer.Error!void {
429     const encoded = digest.hex();
430     try stringField(sink, name, &encoded);
431 }
432 
433 fn objectField(sink: *JsonWriter, name: []const u8) std.Io.Writer.Error!void {
434     try fieldStart(sink, name);
435 }
436 
437 fn countMembers(
438     sink: *JsonWriter,
439     counts: model.Counts,
440 ) std.Io.Writer.Error!void {
441     inline for (comptime std.meta.fieldNames(model.Counts)) |name| {
442         try sink.objectField(name);
443         try sink.write(@field(counts, name));
444     }
445 }
446 
447 fn writeCounts(sink: *JsonWriter, counts: model.Counts) std.Io.Writer.Error!void {
448     try sink.beginObject();
449     try countMembers(sink, counts);
450     try sink.endObject();
451 }
452 
453 fn writeCapacity(
454     sink: *JsonWriter,
455     capacity: limits.Capacity,
456 ) std.Io.Writer.Error!void {
457     try sink.beginObject();
458     try countMembers(sink, model.Counts.fromCapacity(capacity));
459     try capacityMember(sink, "projection_steps_max", capacity.projection_steps_max);
460     try capacityMember(sink, "receipt_rows_max", capacity.receipt_rows_max);
461     try capacityMember(sink, "storage_bytes", capacity.storage_bytes);
462     try sink.endObject();
463 }
464 
465 fn capacityMember(
466     sink: *JsonWriter,
467     name: []const u8,
468     value: usize,
469 ) std.Io.Writer.Error!void {
470     try sink.objectField(name);
471     try sink.write(value);
472 }
473 
474 fn writeProfiles(
475     sink: *JsonWriter,
476     profiles: *const schema.ProfileSet,
477 ) std.Io.Writer.Error!void {
478     try sink.beginObject();
479     try sink.objectField("executable");
480     try writeProfile(sink, &profiles.executable);
481     try sink.objectField("service_trust");
482     try writeProfile(sink, &profiles.service_trust);
483     try sink.objectField("model_origin");
484     try writeProfile(sink, &profiles.model_origin);
485     try sink.objectField("bootstrap");
486     try writeProfile(sink, &profiles.bootstrap);
487     try sink.endObject();
488 }
489 
490 fn writeProfile(
491     sink: *JsonWriter,
492     profile: *const schema.ProfileRef,
493 ) std.Io.Writer.Error!void {
494     try sink.beginObject();
495     try sink.objectField("required");
496     try sink.write(profile.required);
497     try sink.objectField("id");
498     try sink.byteString(profile.id.slice());
499     try sink.objectField("source");
500     try sink.byteString(profile.source.slice());
501     try sink.objectField("body_sha256");
502     const digest = profile.body_sha256.hex();
503     try sink.byteString(&digest);
504     try sink.endObject();
505 }
506 
507 fn phaseField(sink: *JsonWriter, phases: schema.PhaseSet) std.Io.Writer.Error!void {
508     try fieldStart(sink, "phases");
509     try sink.beginArray();
510     inline for (std.meta.tags(schema.Phase)) |phase| {
511         if (phases & schema.phaseBit(phase) != 0) {
512             try sink.byteString(@tagName(phase));
513         }
514     }
515     try sink.endArray();
516 }
517 
518 fn edgeSetField(sink: *JsonWriter, edges: schema.EdgeSet) std.Io.Writer.Error!void {
519     try fieldStart(sink, "traversed_edges");
520     try sink.beginArray();
521     inline for (std.meta.tags(schema.EdgeKind)) |edge| {
522         if (edges & schema.edgeBit(edge) != 0) {
523             try sink.byteString(@tagName(edge));
524         }
525     }
526     try sink.endArray();
527 }