tiny.smg.command.usages
Defined in command.
API (5)
Actions
Public operations.
Types and contracts
Public types and contracts.
Source
Source: tools/smg/src/command/root.zig:23
zig
pub const usages = @import("usages.zig");Source: tools/smg/src/command/usages.zig
zig
const std = @import("std");const pretty_json = @import("pretty").json;const smg = @import("../root.zig");const exports = smg.exports;const graph_mod = smg.graph;const model = smg.model;const options = smg.cli.options;const output = smg.cli.output;const resolve = smg.cli.resolve;const session = smg.command.session;const table = smg.cli.table;const validation = smg.cli.validation;const view = smg.view;pub const Row = struct { node: []const u8, rel: []const u8, file: ?[]const u8, line: ?i64, end_line: ?i64,};pub fn run(allocator: std.mem.Allocator, _: std.mem.Allocator, args: []const []const u8, limits: smg.Limits, boundary: session.PhaseBoundary) !u8 { if (try validation.rejectMissingOptionValues(allocator, args, &.{ "--rel", "--format", "--limit" })) return 2; if (try validation.rejectUnexpectedOptionsPlain(allocator, "usages", "[OPTIONS] NAME", args, &.{ "--rel", "--format", "--limit" })) return 2; if (try validation.rejectInvalidChoiceOption(allocator, "usages", "[OPTIONS] NAME", args, "--format", &.{ "text", "json" })) return 2; if (try validation.rejectInvalidIntegerOption(allocator, "usages", "[OPTIONS] NAME", args, "--limit")) return 2; const pos = try options.positional(allocator, args); if (pos.len == 0) { try output.writeClickUsageError(allocator, "usages", "[OPTIONS] NAME", "Missing argument 'NAME'."); return 2; } if (pos.len > 1) { try output.writeClickUsageError(allocator, "usages", "[OPTIONS] NAME", try validation.unexpectedArgumentMessage(allocator, pos[1..])); return 2; } const root = try smg.storage.requireRoot(allocator); try session.noteGraphAge(allocator, root); var opened = try smg.storage.store.openRead(allocator, root, limits.storage); defer opened.close(); const name = try resolve.storedNode(allocator, root, &opened.reader, pos[0], limits.suggestions); const rel = options.flagValue(args, "--rel"); const node_context = try smg.storage.context.loadFromReader(allocator, &opened.reader, name, true); var source_names: std.ArrayList([]const u8) = .empty; for (node_context.incoming) |edge| { if (rel == null and !exports.isCoupling(edge.rel)) continue; try source_names.append(allocator, edge.source); } const source_nodes = try smg.storage.context.loadNodesFromReader(allocator, &opened.reader, source_names.items); boundary.seal(); defer boundary.beginTeardown(); const usage_rows = try storedRows(allocator, source_nodes, node_context.incoming, rel); const limit = options.signedLimitValue(args); const displayed = options.slicedLen(usage_rows.len, limit); if (std.mem.eql(u8, options.flagValue(args, "--format") orelse "", "json")) return try writeJson(allocator, name, usage_rows[0..displayed], usage_rows.len, limit); if (usage_rows.len == 0) return try output.writeOutFmt(allocator, "{s}: no usages found\n", .{name}); var out: std.Io.Writer.Allocating = .init(allocator); try out.writer.print("Usages of {s} ({d}):\n\n", .{ name, usage_rows.len }); try writeTable(allocator, &out.writer, usage_rows[0..displayed], usage_rows.len); return try output.writeOut(out.written());}fn storedRows(allocator: std.mem.Allocator, source_nodes: []const model.Node, edges: []const model.Edge, rel: ?[]const u8) ![]const Row { var out: std.ArrayList(Row) = .empty; for (edges) |edge| { if (rel == null and !exports.isCoupling(edge.rel)) continue; const source_node = storedNode(source_nodes, edge.source); try out.append(allocator, .{ .node = edge.source, .rel = edge.rel, .file = if (source_node) |node| node.file else null, .line = if (source_node) |node| node.line else null, .end_line = if (source_node) |node| node.end_line else null, }); } std.mem.sort(Row, out.items, {}, rowLess); return try out.toOwnedSlice(allocator);}fn storedNode(nodes: []const model.Node, name: []const u8) ?model.Node { for (nodes) |node| { if (std.mem.eql(u8, node.name, name)) return node; } return null;}pub fn rows(allocator: std.mem.Allocator, graph: graph_mod.Graph, edges: []const model.Edge, rel: ?[]const u8) ![]const Row { var out: std.ArrayList(Row) = .empty; for (edges) |edge| { if (rel == null and !exports.isCoupling(edge.rel)) continue; const source_node = graph_mod.getNode(&graph, edge.source); try out.append(allocator, .{ .node = edge.source, .rel = edge.rel, .file = if (source_node) |node| node.file else null, .line = if (source_node) |node| node.line else null, .end_line = if (source_node) |node| node.end_line else null, }); } std.mem.sort(Row, out.items, {}, rowLess); return try out.toOwnedSlice(allocator);}pub fn writeJson(allocator: std.mem.Allocator, target: []const u8, usage_rows: []const Row, total: usize, limit: i64) !u8 { var out: std.Io.Writer.Allocating = .init(allocator); var writer = pretty_json.Writer.init(&out.writer, .minified); try writer.beginObject(); try writer.objectField("target"); try writer.write(target); try writer.objectField("usages"); try writer.beginArray(); for (usage_rows) |row| { try writer.beginObject(); try writer.objectField("node"); try writer.write(row.node); try writer.objectField("rel"); try writer.write(row.rel); if (row.file) |file| { try writer.objectField("file"); try writer.write(file); } if (row.line) |line| { try writer.objectField("line"); try writer.write(line); } if (row.end_line) |line| { try writer.objectField("end_line"); try writer.write(line); } try writer.endObject(); } try writer.endArray(); try writer.objectField("count"); try writer.write(total); try writer.objectField("displayed"); try writer.write(usage_rows.len); try writer.objectField("truncated"); try writer.write(usage_rows.len < total); try writer.objectField("limit"); try writer.write(limit); try writer.endObject(); try out.writer.writeByte('\n'); return try output.writeOut(out.written());}pub fn writeTable(allocator: std.mem.Allocator, writer: *std.Io.Writer, usage_rows: []const Row, total: usize) !void { const columns = [_]table.Column{ .{ .header = "rel", .max_width = 16 }, .{ .header = "node", .max_width = 48 }, .{ .header = "file", .max_width = 56 }, }; var scratch_state = std.heap.ArenaAllocator.init(allocator); defer scratch_state.deinit(); const scratch = scratch_state.allocator(); var table_rows: std.ArrayList([]const []const u8) = .empty; for (usage_rows) |row| { try table.appendRow(scratch, &table_rows, &.{ row.rel, row.node, try location(scratch, row), }); } try table.writeJsonOption(allocator, writer, &columns, table_rows.items, total, "--format json");}fn rowLess(_: void, a: Row, b: Row) bool { const file_order = std.mem.order(u8, a.file orelse "", b.file orelse ""); if (file_order != .eq) return file_order == .lt; const line_a = a.line orelse 0; const line_b = b.line orelse 0; if (line_a != line_b) return line_a < line_b; const rel_order = std.mem.order(u8, a.rel, b.rel); if (rel_order != .eq) return rel_order == .lt; return std.mem.lessThan(u8, a.node, b.node);}fn location(allocator: std.mem.Allocator, row: Row) ![]const u8 { const file = row.file orelse return ""; if (row.line) |line| { if (row.end_line) |end_line| { if (end_line != line) return try std.fmt.allocPrint(allocator, "{s}:{d}-{d}", .{ file, line, end_line }); } return try std.fmt.allocPrint(allocator, "{s}:{d}", .{ file, line }); } return file;}test "usage rows filter coupling edges and render compact locations" { var arena = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena.deinit(); const allocator = arena.allocator(); var graph = graph_mod.init(allocator); try graph_mod.addNode(&graph, .{ .name = "target", .type = model.NodeType.function, .file = "src/app.py", .line = 1 }); try graph_mod.addNode(&graph, .{ .name = "owner", .type = model.NodeType.module, .file = "src/app.py" }); try graph_mod.addNode(&graph, .{ .name = "caller_a", .type = model.NodeType.function, .file = "src/app.py", .line = 20 }); try graph_mod.addNode(&graph, .{ .name = "caller_b", .type = model.NodeType.function, .file = "src/app.py", .line = 10 }); try graph_mod.addEdge(&graph, .{ .source = "owner", .rel = model.RelType.contains, .target = "target" }); try graph_mod.addEdge(&graph, .{ .source = "caller_a", .rel = model.RelType.calls, .target = "target" }); try graph_mod.addEdge(&graph, .{ .source = "caller_b", .rel = model.RelType.imports, .target = "target" }); const incoming = try view.incoming(graph, allocator, "target", null); const usage_rows = try rows(allocator, graph, incoming, null); try std.testing.expectEqual(@as(usize, 2), usage_rows.len); try std.testing.expectEqualStrings("caller_b", usage_rows[0].node); try std.testing.expectEqualStrings("caller_a", usage_rows[1].node); var out: std.Io.Writer.Allocating = .init(allocator); try out.writer.writeAll("Usages of target (2):\n\n"); try writeTable(allocator, &out.writer, usage_rows, usage_rows.len); try std.testing.expectEqualStrings( \\Usages of target (2): \\ \\rel node file \\------- -------- ------------- \\imports caller_b src/app.py:10 \\calls caller_a src/app.py:20 \\ , out.written()); var truncated: std.Io.Writer.Allocating = .init(allocator); try writeTable(allocator, &truncated.writer, usage_rows[0..1], usage_rows.len); try std.testing.expectEqualStrings( "rel node file\n" ++ "------- -------- -------------\n" ++ "imports caller_b src/app.py:10\n" ++ "(showing 1 of 2 \xE2\x80\x94 use --limit 0 for all, --format json for exact records)\n", truncated.written(), );}Complete call list for command.usages.run
12 direct calls.
tiny.smg.cli.options.flagValue[function] attools/smg/src/cli/options.zig:106tiny.smg.cli.options.positional[function] attools/smg/src/cli/options.zig:35tiny.smg.cli.options.signedLimitValue[function] attools/smg/src/cli/options.zig:145tiny.smg.cli.options.slicedLen[function] attools/smg/src/cli/options.zig:157tiny.smg.cli.output.writeClickUsageError[function] attools/smg/src/cli/output.zig:85tiny.smg.cli.output.writeOut[function] attools/smg/src/cli/output.zig:8tiny.smg.cli.output.writeOutFmt[function] attools/smg/src/cli/output.zig:16tiny.smg.cli.resolve.storedNode[function] attools/smg/src/cli/resolve.zig:20tiny.smg.command.session.noteGraphAge[function] attools/smg/src/command/session.zig:104tools.smg.src.command.usages.storedRows[function] — private; no exact target attools/smg/src/command/usages.zig:64in nearest public ownertiny.smg.command.usagestiny.smg.command.usages.writeJson[function] attools/smg/src/command/usages.zig:105tiny.smg.command.usages.writeTable[function] attools/smg/src/command/usages.zig:147
Audit
| Definitions | 6 |
|---|---|
| Public names | 6 |
| Members | 5 |
| Version | 26.7.0 |
| Revision | daab053ee433 |