tiny.smg.cli.json
Defined in cli.
API (10)
Actions
Public operations.
edgeArrayrenderEdgeArrayrenderStringArraystringArraywriteAboutwriteBetweenwriteFactValuewriteFindingwriteImpactwriteStringArrayValue
Source
Source: tools/smg/src/cli/json.zig
zig
const std = @import("std");const pretty_json = @import("pretty").json;const smg = @import("../root.zig");const facts = smg.cli.facts;const output = smg.cli.output;const model = smg.model;const rules = smg.rules;pub fn writeAbout(writer: *std.Io.Writer, node: model.Node, depth: usize, full: bool, coupling_only: bool, incoming_all: usize, outgoing_all: usize, incoming: []const model.Edge, outgoing: []const model.Edge, path: []const []const u8, neighbors: []const []const u8) !void { var out = pretty_json.Writer.init(writer, .minified); try out.beginObject(); try out.objectField("node"); try model.writeNodeDataJsonObject(node, &out); var incoming_display = incoming; var outgoing_display = outgoing; var neighbors_display = neighbors; if (!full) { if (incoming_display.len > 10) incoming_display = incoming_display[0..10]; if (outgoing_display.len > 10) outgoing_display = outgoing_display[0..10]; if (neighbors_display.len > 10) neighbors_display = neighbors_display[0..10]; } if (depth >= 1) { try out.objectField("containment_path"); try writeStringArrayValue(&out, path); try out.objectField("incoming"); try out.beginArray(); for (incoming_display) |edge| { try out.beginObject(); try out.objectField("source"); try out.write(edge.source); try out.objectField("rel"); try out.write(edge.rel); try out.endObject(); } try out.endArray(); try out.objectField("outgoing"); try out.beginArray(); for (outgoing_display) |edge| { try out.beginObject(); try out.objectField("target"); try out.write(edge.target); try out.objectField("rel"); try out.write(edge.rel); try out.endObject(); } try out.endArray(); try out.objectField("incoming_total"); try out.write(incoming.len); try out.objectField("outgoing_total"); try out.write(outgoing.len); const incoming_truncated = incoming.len - incoming_display.len; const outgoing_truncated = outgoing.len - outgoing_display.len; const neighbors_truncated = neighbors.len - neighbors_display.len; const edge_truncated = !full and (incoming_truncated != 0 or outgoing_truncated != 0); const neighbor_truncated = !full and depth >= 2 and neighbors_truncated != 0; if (edge_truncated) { try out.objectField("truncated"); try out.beginObject(); if (incoming_truncated != 0) { try out.objectField("incoming"); try out.write(incoming_truncated); } if (outgoing_truncated != 0) { try out.objectField("outgoing"); try out.write(outgoing_truncated); } if (neighbor_truncated) { try out.objectField("neighbors"); try out.write(neighbors_truncated); } try out.endObject(); } if (coupling_only) { try out.objectField("hidden_rels"); try out.beginObject(); try out.objectField("incoming"); try out.write(incoming_all - incoming.len); try out.objectField("outgoing"); try out.write(outgoing_all - outgoing.len); try out.endObject(); } if (depth >= 2) { try out.objectField("neighbors"); try writeStringArrayValue(&out, neighbors_display); try out.objectField("neighbors_total"); try out.write(neighbors.len); if (neighbor_truncated and !edge_truncated) { try out.objectField("truncated"); try out.beginObject(); try out.objectField("neighbors"); try out.write(neighbors_truncated); try out.endObject(); } } } try out.endObject(); try writer.writeByte('\n');}pub fn writeImpact(writer: *std.Io.Writer, target: []const u8, names: []const []const u8, total: usize, limit: i64, coupling_only: bool) !void { var out = pretty_json.Writer.init(writer, .minified); try out.beginObject(); try out.objectField("target"); try out.write(target); try out.objectField("affected"); try writeStringArrayValue(&out, names); try out.objectField("count"); try out.write(total); try out.objectField("displayed"); try out.write(names.len); try out.objectField("truncated"); try out.write(names.len < total); try out.objectField("limit"); try out.write(limit); try out.objectField("coupling_only"); try out.write(coupling_only); try out.endObject(); try writer.writeByte('\n');}pub fn writeBetween(writer: *std.Io.Writer, source: []const u8, target: []const u8, path: ?[]const []const u8, direct: []const model.Edge) !void { var out = pretty_json.Writer.init(writer, .minified); try out.beginObject(); try out.objectField("source"); try out.write(source); try out.objectField("target"); try out.write(target); try out.objectField("path"); if (path) |items| { try writeStringArrayValue(&out, items); } else { try out.write(null); } try out.objectField("direct_edges"); try out.beginArray(); for (direct) |edge| { try out.beginObject(); try out.objectField("source"); try out.write(edge.source); try out.objectField("rel"); try out.write(edge.rel); try out.objectField("target"); try out.write(edge.target); try out.endObject(); } try out.endArray(); try out.endObject(); try writer.writeByte('\n');}pub fn stringArray(allocator: std.mem.Allocator, values: []const []const u8) !u8 { return try output.writeOut(try renderStringArray(allocator, values));}pub fn renderStringArray(allocator: std.mem.Allocator, values: []const []const u8) ![]const u8 { var out: std.Io.Writer.Allocating = .init(allocator); errdefer out.deinit(); var writer = pretty_json.Writer.init(&out.writer, .minified); try writeStringArrayValue(&writer, values); try out.writer.writeByte('\n'); return try out.toOwnedSlice();}pub fn edgeArray(allocator: std.mem.Allocator, edges: []const model.Edge) !u8 { return try output.writeOut(try renderEdgeArray(allocator, edges));}pub fn renderEdgeArray(allocator: std.mem.Allocator, edges: []const model.Edge) ![]const u8 { var out: std.Io.Writer.Allocating = .init(allocator); errdefer out.deinit(); var writer = pretty_json.Writer.init(&out.writer, .minified); try writer.beginArray(); for (edges) |edge| try model.writeEdgeDataJsonObject(edge, &writer); try writer.endArray(); try out.writer.writeByte('\n'); return try out.toOwnedSlice();}pub fn writeFinding(allocator: std.mem.Allocator, writer: *pretty_json.Writer, finding: rules.Finding) !void { try writer.beginObject(); try writer.objectField("rule"); try writer.write(finding.rule); try writer.objectField("type"); try writer.write(finding.type); try writer.objectField("message"); try writer.write(finding.message); try writer.objectField("witnesses"); try writer.beginArray(); if (finding.predicates.len != 0) { for (finding.predicates) |predicate| { try writer.beginObject(); try writer.objectField("kind"); try writer.write("predicate"); try writer.objectField("subject"); try writer.write(predicate.subject); try writer.objectField("assertion"); try writer.write(predicate.assertion); try writer.objectField("facts"); try writer.beginObject(); for (predicate.facts) |fact| { try writer.objectField(fact.name); try writeFactValue(allocator, writer, fact.value); } try writer.endObject(); try writer.endObject(); } } else if (finding.edges.len != 0) { try writer.beginObject(); try writer.objectField("kind"); try writer.write("edge"); try writer.objectField("edges"); try writer.beginArray(); for (finding.edges) |edge| { try writer.beginObject(); try writer.objectField("source"); try writer.write(edge.source); try writer.objectField("rel"); try writer.write(edge.rel); try writer.objectField("target"); try writer.write(edge.target); try writer.endObject(); } try writer.endArray(); try writer.endObject(); } else if (finding.nodes.len != 0) { try writer.beginObject(); try writer.objectField("kind"); try writer.write("node"); try writer.objectField("nodes"); try writeStringArrayValue(writer, finding.nodes); try writer.endObject(); } try writer.endArray(); try writer.endObject();}pub fn writeFactValue(allocator: std.mem.Allocator, writer: *pretty_json.Writer, value: rules.FactValue) !void { switch (value) { .number => |number| { if (number == @floor(number)) { try writer.write(@as(i64, @intFromFloat(number))); } else { try writer.write(number); } }, .float_number => |number| { const raw = try facts.floatText(allocator, number); defer allocator.free(raw); try writer.raw(raw); }, .boolean => |boolean| try writer.write(boolean), }}pub fn writeStringArrayValue(writer: *pretty_json.Writer, values: []const []const u8) !void { try writer.beginArray(); for (values) |value| try writer.write(value); try writer.endArray();}test "about json preserves coupling contract" { var out: std.Io.Writer.Allocating = .init(std.testing.allocator); defer out.deinit(); const node = model.Node{ .name = "app.main", .type = model.NodeType.function, .file = "src/app.py", .line = 1, .docstring = "main doc\nmore" }; const incoming = [_]model.Edge{}; const outgoing = [_]model.Edge{.{ .source = "app.main", .rel = model.RelType.calls, .target = "app.helper" }}; const path = [_][]const u8{ "app", "app.main" }; try writeAbout(&out.writer, node, 1, false, true, 1, 1, &incoming, &outgoing, &path, &.{}); try std.testing.expectEqualStrings( \\{"node":{"name":"app.main","type":"function","file":"src/app.py","line":1,"docstring":"main doc\nmore"},"containment_path":["app","app.main"],"incoming":[],"outgoing":[{"target":"app.helper","rel":"calls"}],"incoming_total":0,"outgoing_total":1,"hidden_rels":{"incoming":1,"outgoing":0}} \\ , out.written());}test "impact and between json preserve command schemas" { var out: std.Io.Writer.Allocating = .init(std.testing.allocator); defer out.deinit(); try writeImpact(&out.writer, "app.helper", &.{"app.main"}, 1, 20, true); const path = [_][]const u8{ "a", "b" }; const direct = [_]model.Edge{.{ .source = "a", .rel = model.RelType.calls, .target = "b" }}; try writeBetween(&out.writer, "a", "b", &path, &direct); try writeBetween(&out.writer, "a", "d", null, &.{}); try std.testing.expectEqualStrings( "{\"target\":\"app.helper\",\"affected\":[\"app.main\"],\"count\":1,\"displayed\":1,\"truncated\":false,\"limit\":20,\"coupling_only\":true}\n" ++ "{\"source\":\"a\",\"target\":\"b\",\"path\":[\"a\",\"b\"],\"direct_edges\":[{\"source\":\"a\",\"rel\":\"calls\",\"target\":\"b\"}]}\n" ++ "{\"source\":\"a\",\"target\":\"d\",\"path\":null,\"direct_edges\":[]}\n", out.written(), );}test "array renderers preserve minified newline output" { const allocator = std.testing.allocator; const strings = try renderStringArray(allocator, &.{ "a", "b" }); defer allocator.free(strings); try std.testing.expectEqualStrings("[\"a\",\"b\"]\n", strings); const edges = [_]model.Edge{.{ .source = "a", .rel = model.RelType.calls, .target = "b" }}; const edge_json = try renderEdgeArray(allocator, &edges); defer allocator.free(edge_json); try std.testing.expectEqualStrings("[{\"source\":\"a\",\"rel\":\"calls\",\"target\":\"b\"}]\n", edge_json);}test "finding json preserves quantified predicate witness contract" { const allocator = std.testing.allocator; const fact = rules.PredicateFact{ .name = "fan_out", .value = .{ .number = 1 } }; const predicate = rules.PredicateWitness{ .subject = "core.lib", .assertion = "fan_out <= 0", .facts = &.{fact} }; const finding = rules.Finding{ .rule = "fan-out", .type = "quantified", .message = "1 subject(s) failed fan_out <= 0", .nodes = &.{"core.lib"}, .predicates = &.{predicate}, }; var out: std.Io.Writer.Allocating = .init(allocator); defer out.deinit(); var writer = pretty_json.Writer.init(&out.writer, .minified); try writeFinding(allocator, &writer, finding); try std.testing.expectEqualStrings( \\{"rule":"fan-out","type":"quantified","message":"1 subject(s) failed fan_out <= 0","witnesses":[{"kind":"predicate","subject":"core.lib","assertion":"fan_out <= 0","facts":{"fan_out":1}}]} , out.written());}Source: tools/smg/src/cli/root.zig:4
zig
pub const json = @import("json.zig");Audit
| Definitions | 11 |
|---|---|
| Public names | 11 |
| Members | 0 |
| Version | 26.7.0 |
| Revision | daab053ee433 |