tiny.smg.command.rules.add
Defined in command.rules.
API (1)
Actions
Public operations.
Source
Source: tools/smg/src/command/rules/add.zig
zig
const std = @import("std");const smg = @import("../../root.zig");const model = smg.model;const options = smg.cli.options;const output = smg.cli.output;const rules_mod = smg.rules;const text = smg.text;const QuantifiedAdd = struct { selector: []const u8, assertion: []const u8,};const AddKind = union(enum) { deny: []const u8, invariant: []const u8, quantified: QuantifiedAdd,};pub fn parse(allocator: std.mem.Allocator, args: []const []const u8, name: []const u8) !rules_mod.Rule { return switch (try addKind(allocator, args)) { .deny => |pattern| try denyRule(allocator, args, name, pattern), .invariant => |invariant| try invariantRule(allocator, args, name, invariant), .quantified => |quantified| try quantifiedRule(allocator, args, name, quantified), };}fn addKind(allocator: std.mem.Allocator, args: []const []const u8) !AddKind { const deny = options.flagValue(args, "--deny"); const exclude_source = options.flagValue(args, "--exclude-source"); const allow_empty_target = options.hasFlag(args, "--allow-empty-target"); const invariant = options.flagValue(args, "--invariant"); const forall = options.flagValue(args, "--forall"); const assertion = options.flagValue(args, "--assert"); var kind_count: usize = 0; if (deny != null) kind_count += 1; if (invariant != null) kind_count += 1; if (forall != null) kind_count += 1; if (kind_count > 1) { try output.writeErrFmt(allocator, "Error: specify exactly one of --deny, --invariant, or --forall.\n", .{}); return error.RuleValidation; } if (kind_count == 0) { try output.writeErrFmt(allocator, "Error: specify one of --deny, --invariant, or --forall.\n", .{}); return error.RuleValidation; } if (forall != null and assertion == null) { try output.writeErrFmt(allocator, "Error: --assert is required with --forall.\n", .{}); return error.RuleValidation; } if (forall == null and assertion != null) { try output.writeErrFmt(allocator, "Error: --assert may only be used with --forall.\n", .{}); return error.RuleValidation; } if (deny == null and exclude_source != null) { try output.writeErrFmt( allocator, "Error: --exclude-source may only be used with --deny.\n", .{}, ); return error.RuleValidation; } if (deny == null and allow_empty_target) { try output.writeErrFmt( allocator, "Error: --allow-empty-target may only be used with --deny.\n", .{}, ); return error.RuleValidation; } if (deny) |pattern| return .{ .deny = pattern }; if (invariant) |value| return .{ .invariant = value }; return .{ .quantified = .{ .selector = forall.?, .assertion = assertion.? } };}fn denyRule(allocator: std.mem.Allocator, args: []const []const u8, name: []const u8, pattern: []const u8) !rules_mod.Rule { _ = rules_mod.parseDenyPattern(pattern) catch |err| { if (err == error.InvalidDenyPattern) { try output.writeErrFmt(allocator, "Error: invalid deny pattern: '{s}'\n", .{pattern}); return error.RuleValidation; } return err; }; return .{ .name = try allocator.dupe(u8, name), .type = try allocator.dupe(u8, "deny"), .pattern = try allocator.dupe(u8, pattern), .exclude_source = if (options.flagValue(args, "--exclude-source")) |value| try allocator.dupe(u8, value) else null, .allow_empty_target = options.hasFlag(args, "--allow-empty-target"), .scope = try ruleScope(allocator, args), };}fn invariantRule(allocator: std.mem.Allocator, args: []const []const u8, name: []const u8, invariant: []const u8) !rules_mod.Rule { if (options.flagValue(args, "--baseline")) |baseline| { if (!std.mem.eql(u8, invariant, "namespace-handle-imports")) { try output.writeErrFmt( allocator, "Error: --baseline may only be used with the namespace-handle-imports invariant.\n", .{}, ); return error.RuleValidation; } rules_mod.validateNamespaceHandleImportBaseline(baseline) catch { try output.writeErrFmt(allocator, "Error: --baseline must be COUNT:SHA256.\n", .{}); return error.RuleValidation; }; } return .{ .name = try allocator.dupe(u8, name), .type = try allocator.dupe(u8, "invariant"), .invariant = try allocator.dupe(u8, invariant), .params = try ruleParams(allocator, args), .scope = try ruleScope(allocator, args), };}fn quantifiedRule(allocator: std.mem.Allocator, args: []const []const u8, name: []const u8, quantified: QuantifiedAdd) !rules_mod.Rule { const unknown_metrics = rules_mod.unknownAssertionMetrics(allocator, quantified.assertion) catch |err| { if (try rules_mod.assertionErrorMessage(allocator, quantified.assertion, err)) |message| { try output.writeErrFmt(allocator, "Error: {s}\n", .{message}); return error.RuleValidation; } return err; }; if (unknown_metrics.len != 0) { try output.writeErrFmt(allocator, "Error: unknown metric identifier(s) in rule '{s}': {s}\n", .{ name, try text.list.joinStrings(allocator, unknown_metrics, ", ") }); return error.RuleValidation; } return .{ .name = try allocator.dupe(u8, name), .type = try allocator.dupe(u8, "quantified"), .selector = try allocator.dupe(u8, quantified.selector), .assertion = try allocator.dupe(u8, quantified.assertion), .scope = try ruleScope(allocator, args), };}fn ruleScope(allocator: std.mem.Allocator, args: []const []const u8) !?[]const u8 { return if (options.flagValue(args, "--scope")) |value| try allocator.dupe(u8, value) else null;}fn ruleParams(allocator: std.mem.Allocator, args: []const []const u8) ![]const model.Pair { var out: std.ArrayList(model.Pair) = .empty; if (options.flagValue(args, "--entry-points")) |entry| { try out.append(allocator, .{ .key = try allocator.dupe(u8, "entry_points"), .value = try allocator.dupe(u8, entry) }); } if (options.flagValue(args, "--baseline")) |baseline| { try out.append(allocator, .{ .key = try allocator.dupe(u8, "baseline"), .value = try allocator.dupe(u8, baseline) }); } return try out.toOwnedSlice(allocator);}test "parse add builds invariant rule params" { var arena = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena.deinit(); const allocator = arena.allocator(); const args = &.{ "add", "live", "--invariant", "no-dead-code", "--entry-points", "main,cli.*", "--scope", "tools.smg.*" }; const rule = try parse(allocator, args, "live"); try std.testing.expectEqualStrings("live", rule.name); try std.testing.expectEqualStrings("invariant", rule.type); try std.testing.expectEqualStrings("no-dead-code", rule.invariant.?); try std.testing.expectEqualStrings("tools.smg.*", rule.scope.?); try std.testing.expectEqual(@as(usize, 1), rule.params.len); try std.testing.expectEqualStrings("entry_points", rule.params[0].key); try std.testing.expectEqualStrings("main,cli.*", rule.params[0].value);}test "parse add builds namespace import baseline" { var arena = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena.deinit(); const allocator = arena.allocator(); const args = &.{ "add", "root-handles", "--invariant", "namespace-handle-imports", "--baseline", "3:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", }; const rule = try parse(allocator, args, "root-handles"); try std.testing.expectEqual(@as(usize, 1), rule.params.len); try std.testing.expectEqualStrings("baseline", rule.params[0].key); try std.testing.expectEqualStrings( "3:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", rule.params[0].value, );}test "parse add rejects namespace import baseline for other invariants" { var arena = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena.deinit(); const allocator = arena.allocator(); const args = &.{ "add", "acyclic", "--invariant", "no-cycles", "--baseline", "0:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" }; try std.testing.expectError(error.RuleValidation, parse(allocator, args, "acyclic"));}test "parse add rejects incomplete namespace import baseline" { var arena = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena.deinit(); const allocator = arena.allocator(); const args = &.{ "add", "root-handles", "--invariant", "namespace-handle-imports", "--baseline", "0:00" }; try std.testing.expectError(error.RuleValidation, parse(allocator, args, "root-handles"));}test "parse add builds deny dead-target policy" { var arena = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena.deinit(); const allocator = arena.allocator(); const args = &.{ "add", "no-back-root", "--deny", "pkg.feature.* -[imports]-> pkg.feature.root", "--exclude-source", "*.test", "--allow-empty-target", }; const rule = try parse(allocator, args, "no-back-root"); try std.testing.expectEqualStrings("deny", rule.type); try std.testing.expectEqualStrings( "pkg.feature.* -[imports]-> pkg.feature.root", rule.pattern.?, ); try std.testing.expectEqualStrings("*.test", rule.exclude_source.?); try std.testing.expect(rule.allow_empty_target);}test "parse add rejects empty-target policy for non-deny rules" { var arena = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena.deinit(); const allocator = arena.allocator(); const args = &.{ "add", "acyclic", "--invariant", "no-cycles", "--allow-empty-target" }; try std.testing.expectError(error.RuleValidation, parse(allocator, args, "acyclic"));}Source: tools/smg/src/command/rules/root.zig:1
zig
pub const add = @import("add.zig");Complete caller list for command.rules.add.parse
7 direct callers.
tools.smg.src.command.rules.add.test_parse_add_builds_deny_dead-target_policy[function] — test; no exact target attools/smg/src/command/rules/add.zig:210in nearest public ownertiny.smg.command.rules.addtools.smg.src.command.rules.add.test_parse_add_builds_invariant_rule_params[function] — test; no exact target attools/smg/src/command/rules/add.zig:158in nearest public ownertiny.smg.command.rules.addtools.smg.src.command.rules.add.test_parse_add_builds_namespace_import_baseline[function] — test; no exact target attools/smg/src/command/rules/add.zig:173in nearest public ownertiny.smg.command.rules.addtools.smg.src.command.rules.add.test_parse_add_rejects_empty-target_policy_for_non-deny_rules[function] — test; no exact target attools/smg/src/command/rules/add.zig:233in nearest public ownertiny.smg.command.rules.addtools.smg.src.command.rules.add.test_parse_add_rejects_incomplete_namespace_import_baseline[function] — test; no exact target attools/smg/src/command/rules/add.zig:202in nearest public ownertiny.smg.command.rules.addtools.smg.src.command.rules.add.test_parse_add_rejects_namespace_import_baseline_for_other_invariants[function] — test; no exact target attools/smg/src/command/rules/add.zig:194in nearest public ownertiny.smg.command.rules.addtools.smg.src.command.rules.command.addRule[function] — private; no exact target attools/smg/src/command/rules/command.zig:80in nearest public ownertiny.smg.command.rules.command
Audit
| Definitions | 2 |
|---|---|
| Public names | 3 |
| Members | 0 |
| Version | 26.7.0 |
| Revision | daab053ee433 |