Skip to documentation
SLOP

tiny.smg.command.usages

Reference tiny.smg command usages

Defined in command.

API (5)

Actions

Public operations.

Types and contracts

Public types and contracts.

No direct callersNo direct callscommandusages
Static calls · unresolved targets: unknown · external targets: unknown.

Source

Called byCallsNo direct callstest; no linktools.smg.src.command.usagestest: usage rows filter coupling edge...command.usagesrows
Static calls · unresolved targets: 2 · external targets: 2.
Called byCallsNo direct callerscli.optionsflagValuecli.optionspositionalcli.optionssignedLimitValuecli.optionsslicedLencli.outputwriteClickUsageError+7 morecommand.usagesrun
Static calls · unresolved targets: 3 · external targets: 13.
Called byCallscommand.usagesruncli.outputwriteOutcommand.usageswriteJson
Static calls · unresolved targets: 1 · external targets: 8.
Called byCallscommand.usagesruntest; no linktools.smg.src.command.usagestest: usage rows filter coupling edge...private sourcelib.markdown.src.tableappendRowprivate; no linktools.smg.src.command.usageslocationcommand.usageswriteTable
Static calls · unresolved targets: 0 · external targets: 3.

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.

Audit

Definitions6
Public names6
Members5
Version26.7.0
Revisiondaab053ee433