tiny.smg.cli.dispatch
Defined in cli.
API (3)
Actions
Public operations.
Types and contracts
Public types and contracts.
Source
Source: tools/smg/src/cli/dispatch.zig
zig
const std = @import("std");const pretty_usage = @import("pretty_usage");const smg = @import("../root.zig");const cli_about = smg.command.about;const cli_analyze = smg.command.analyze;const cli_batch = smg.command.batch;const cli_between = smg.command.between;const cli_blame = smg.command.blame;const cli_calls = smg.command.calls;const cli_check = smg.command.check;const cli_concepts = smg.command.concepts;const cli_context = smg.command.context;const cli_diff = smg.command.diff;const cli_exports = smg.command.exports;const cli_impact = smg.command.impact;const cli_init = smg.command.init;const cli_mutate = smg.command.mutate;const cli_node = smg.command.node;const cli_output = smg.cli.output;const cli_options = smg.cli.options;const cli_query = smg.command.query;const cli_rules = smg.command.rules;const cli_scan = smg.command.scan;const cli_search = smg.command.search;const cli_session = smg.command.session;const cli_status = smg.command.status;const cli_usages = smg.command.usages;const cli_validate = smg.command.validate;const cli_watch = smg.command.watch;const help_data = @import("../help/root.zig");const click_help = help_data.click;const help = help_data.help;const CommandRun = *const fn (std.mem.Allocator, std.mem.Allocator, []const []const u8, smg.Limits) anyerror!u8;const PhasedCommandRun = *const fn (std.mem.Allocator, std.mem.Allocator, []const []const u8, smg.Limits, cli_session.PhaseBoundary) anyerror!u8;const BatchCommandRun = *const fn (std.mem.Allocator, std.mem.Allocator, []const []const u8, smg.Limits) anyerror!u8;const CommandAction = union(enum) { regular: CommandRun, phased: PhasedCommandRun, batch: BatchCommandRun,};pub const Build = struct { version: []const u8,};pub fn diagnosedErrorExitCode(err: anyerror) ?u8 { if (err == error.NodeNotFound or err == error.AmbiguousName) return 1; return null;}const EmptyHelp = enum { none, query, export_root, concept,};const Command = struct { name: []const u8, action: CommandAction, empty_help: EmptyHelp = .none,};const commands = [_]Command{ .{ .name = "init", .action = .{ .regular = cli_init.run } }, .{ .name = "scan", .action = .{ .regular = cli_scan.run } }, .{ .name = "add", .action = .{ .regular = cli_mutate.add } }, .{ .name = "link", .action = .{ .regular = cli_mutate.link } }, .{ .name = "rm", .action = .{ .regular = cli_mutate.remove } }, .{ .name = "unlink", .action = .{ .regular = cli_mutate.unlink } }, .{ .name = "update", .action = .{ .regular = cli_mutate.update } }, .{ .name = "status", .action = .{ .phased = cli_status.run } }, .{ .name = "list", .action = .{ .phased = cli_node.list } }, .{ .name = "show", .action = .{ .phased = cli_node.show } }, .{ .name = "about", .action = .{ .phased = cli_about.run } }, .{ .name = "calls", .action = .{ .phased = cli_calls.run } }, .{ .name = "usages", .action = .{ .phased = cli_usages.run } }, .{ .name = "impact", .action = .{ .phased = cli_impact.run } }, .{ .name = "between", .action = .{ .phased = cli_between.run } }, .{ .name = "query", .action = .{ .phased = cli_query.run }, .empty_help = .query }, .{ .name = "export", .action = .{ .phased = cli_exports.run }, .empty_help = .export_root }, .{ .name = "analyze", .action = .{ .phased = cli_analyze.run } }, .{ .name = "overview", .action = .{ .phased = cli_analyze.overview } }, .{ .name = "search", .action = .{ .phased = cli_search.run } }, .{ .name = "index", .action = .{ .regular = cli_search.index } }, .{ .name = "validate", .action = .{ .phased = cli_validate.run } }, .{ .name = "rule", .action = .{ .phased = cli_rules.run } }, .{ .name = "check", .action = .{ .phased = cli_check.command.run } }, .{ .name = "refresh-check", .action = .{ .regular = cli_check.refresh.run } }, .{ .name = "concept", .action = .{ .phased = cli_concepts.run }, .empty_help = .concept }, .{ .name = "context", .action = .{ .phased = cli_context.run } }, .{ .name = "blame", .action = .{ .phased = cli_blame.run } }, .{ .name = "diff", .action = .{ .phased = cli_diff.run } }, .{ .name = "watch", .action = .{ .regular = cli_watch.run } }, .{ .name = "batch", .action = .{ .batch = cli_batch.run } },};pub fn run( allocator: std.mem.Allocator, phases: std.mem.Allocator, args: []const []const u8, command_name: []const u8, build: Build, limits: smg.Limits, boundary: cli_session.PhaseBoundary,) !u8 { if (args.len < 1) { try click_help.writeRoot(allocator, command_name); return 2; } if (std.mem.eql(u8, args[0], "--help")) { try click_help.writeRoot(allocator, command_name); return 0; } if (std.mem.eql(u8, args[0], "--version")) return try writeVersion(allocator, args, command_name, build.version); if (std.mem.startsWith(u8, args[0], "-")) { try cli_output.writeRootUsageError(allocator, command_name, try std.fmt.allocPrint(allocator, "No such option: {s}", .{cli_options.optionName(args[0])})); return 2; } const command = args[0]; const rest = args[1..]; const entry = commandEntry(command) orelse { try cli_output.writeRootUsageError(allocator, command_name, try std.fmt.allocPrint(allocator, "No such command '{s}'.", .{command})); return 2; }; if (click_help.hasLongArg(rest)) { if (try click_help.writeForCommandPath(allocator, args)) |code| return code; return try writeCommandHelp(allocator, args); } return try runCommand( allocator, phases, entry, rest, limits, boundary, );}fn commandEntry(command: []const u8) ?Command { for (commands) |entry| if (std.mem.eql(u8, entry.name, command)) return entry; return null;}fn runCommand( allocator: std.mem.Allocator, phases: std.mem.Allocator, command: Command, args: []const []const u8, limits: smg.Limits, boundary: cli_session.PhaseBoundary,) !u8 { if (args.len == 0) { if (try writeEmptyHelp(allocator, command.empty_help)) |code| return code; } return switch (command.action) { .regular => |run_regular| try run_regular(allocator, phases, args, limits), .phased => |run_phased| try run_phased(allocator, phases, args, limits, boundary), .batch => |run_batch| try run_batch(allocator, phases, args, limits), };}fn writeEmptyHelp(allocator: std.mem.Allocator, empty_help: EmptyHelp) !?u8 { switch (empty_help) { .none => return null, .query => try click_help.writeQueryRoot(allocator), .export_root => try click_help.writeExportRoot(allocator), .concept => try click_help.writeConceptRoot(allocator), } return 2;}fn writeVersion(allocator: std.mem.Allocator, args: []const []const u8, command_name: []const u8, version: []const u8) !u8 { for (args[1..]) |arg| { if (!std.mem.startsWith(u8, arg, "-")) break; if (std.mem.eql(u8, arg, "--help")) continue; try cli_output.writeRootUsageError(allocator, command_name, try std.fmt.allocPrint(allocator, "No such option: {s}", .{cli_options.optionName(arg)})); return 2; } return try cli_output.writeOutFmt(allocator, "smg, version {s}\n", .{version});}fn writeCommandHelp(allocator: std.mem.Allocator, args: []const []const u8) !u8 { var stdout_buffer: [8192]u8 = undefined; var stdout_writer = std.Io.File.stdout().writer(std.Options.debug_io, &stdout_buffer); defer stdout_writer.interface.flush() catch {}; var stderr_buffer: [8192]u8 = undefined; var stderr_writer = std.Io.File.stderr().writer(std.Options.debug_io, &stderr_buffer); defer stderr_writer.interface.flush() catch {}; return try pretty_usage.writeCommandPathHelpOrError( allocator, &stdout_writer.interface, &stderr_writer.interface, help, args, .{ .layout = .{ .width = 88 }, .suggestions = .{ .enabled = true }, .parent_fallback_help = true, }, );}test "top usage includes historical python command surface" { const rendered = try pretty_usage.renderHelpAlloc(std.testing.allocator, help, .{ .layout = .{ .width = 120 } }); defer std.testing.allocator.free(rendered); for ([_][]const u8{ "calls NAME", "diff [REF]", "context NAME", "blame NAME", "index", "batch", }) |needle| { try std.testing.expect(std.mem.indexOf(u8, rendered, needle) != null); }}test "diagnosed resolver errors exit without process label" { try std.testing.expectEqual( @as(?u8, 1), diagnosedErrorExitCode(error.NodeNotFound), ); try std.testing.expectEqual( @as(?u8, 1), diagnosedErrorExitCode(error.AmbiguousName), ); try std.testing.expect( diagnosedErrorExitCode(error.MissingArgument) == null, );}test "command usage resolves before project loading" { const diff_detail = help.commandHelp("diff").?; const diff_rendered = try pretty_usage.renderHelpAlloc(std.testing.allocator, diff_detail.*, .{ .layout = .{ .width = 120 } }); defer std.testing.allocator.free(diff_rendered); try std.testing.expect(std.mem.indexOf(u8, diff_rendered, "usage: smg diff [REF] [--json]") != null); try std.testing.expect(std.mem.indexOf(u8, diff_rendered, "Compare the current graph against a git ref.") != null); const batch_detail = help.commandHelp("batch").?; const batch_rendered = try pretty_usage.renderHelpAlloc(std.testing.allocator, batch_detail.*, .{ .layout = .{ .width = 120 } }); defer std.testing.allocator.free(batch_rendered); try std.testing.expect(std.mem.indexOf(u8, batch_rendered, "usage: smg batch [--format text|json]") != null); try std.testing.expect(std.mem.indexOf(u8, batch_rendered, "Execute JSONL graph mutations from stdin.") != null); try std.testing.expect(std.mem.indexOf(u8, batch_rendered, "Read up to 64 MiB inclusive from stdin.") != null); try std.testing.expect(help.commandHelp("unknown") == null);}test "scan usage omits removed jobs option" { const scan_detail = help.commandHelp("scan").?; const scan_rendered = try pretty_usage.renderHelpAlloc( std.testing.allocator, scan_detail.*, .{ .layout = .{ .width = 120 } }, ); defer std.testing.allocator.free(scan_rendered); try std.testing.expect(std.mem.indexOf(u8, scan_rendered, "--jobs") == null);}test "one-shot queries and mutations have distinct phase actions" { for ([_][]const u8{ "status", "list", "show", "about", "calls", "usages", "impact", "between", "query", "export", "analyze", "overview", "search", "validate", "rule", "check", "concept", "context", "blame", "diff", }) |name| { const entry = commandEntry(name).?; try std.testing.expectEqual(@as(std.meta.Tag(CommandAction), .phased), std.meta.activeTag(entry.action)); } for ([_][]const u8{ "init", "scan", "add", "link", "rm", "unlink", "update", "index", "refresh-check", "watch", }) |name| { const entry = commandEntry(name).?; try std.testing.expectEqual(@as(std.meta.Tag(CommandAction), .regular), std.meta.activeTag(entry.action)); }}test "batch is the sole SMG command action with input storage" { comptime { @stardustClaim( @import("alloc_phase").capacity.witness(@import("../batch/input/root.zig").Storage, "smg_batch_input_cli_composition"), null, null, null, null, null, null, ); } for (commands) |entry| { try std.testing.expectEqual( std.mem.eql(u8, entry.name, "batch"), std.meta.activeTag(entry.action) == .batch, ); }}Source: tools/smg/src/cli/root.zig:1
zig
pub const dispatch = @import("dispatch.zig");Audit
| Definitions | 4 |
|---|---|
| Public names | 4 |
| Members | 1 |
| Version | 26.7.0 |
| Revision | daab053ee433 |