Skip to documentation
SLOP

tiny.smg.scan.reconcile.projection

Reference tiny.smg scan reconcile projection

Defined in scan.reconcile.

API (5)

Actions

Public operations.

Types and contracts

Public types and contracts.

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

Source

Called byCallsprivate; no linktools.smg.src.scan.reconcile.projectioncheckClonePlanAllocationFailuresmodelcloneEdgemodeldeinitEdgescan.reconcile.projectionclonePlan
Static calls · unresolved targets: 0 · external targets: 3.
Called byCallsscan.reconcile.projectionfinishmodelsourcePairscan.reconcile.projectionensurePackageHierarchy
Static calls · unresolved targets: 1 · external targets: 5.
Called byCallsNo direct callersscan.reconcile.projectionensurePackageHierarchyprivate; no linktools.smg.src.scan.reconcile.projectionhierarchyEdgeprivate; no linktools.smg.src.scan.reconcile.projectionmanualEdgescan.reconcile.projectionfinish
Static calls · unresolved targets: 1 · external targets: 2.
Called byCallsNo direct callersprivate; no linktools.smg.src.scan.reconcile.projectionretirescan.reconcile.scopecollectscan.reconcile.projectionprepare
Static calls · unresolved targets: 0 · external targets: 0.

Source: tools/smg/src/scan/reconcile/projection.zig

zig
const std = @import("std");const sys = @import("sys");const smg = @import("../../root.zig");const reconcile = @import("root.zig");const graph_mod = smg.graph;const model = smg.model;const scan_root = smg.scan;const core = scan_root.core;pub const Plan = struct {    files: []const []const u8,    displaced_edges: []const model.Edge,};pub fn clonePlan(allocator: std.mem.Allocator, plan: Plan) !Plan {    const files = try allocator.alloc([]const u8, plan.files.len);    var file_count: usize = 0;    errdefer {        for (files[0..file_count]) |file| allocator.free(file);        allocator.free(files);    }    for (plan.files, 0..) |file, index| {        files[index] = try allocator.dupe(u8, file);        file_count += 1;    }    const edges = try allocator.alloc(model.Edge, plan.displaced_edges.len);    var edge_count: usize = 0;    errdefer {        for (edges[0..edge_count]) |*edge| model.deinitEdge(edge, allocator);        allocator.free(edges);    }    for (plan.displaced_edges, 0..) |edge, index| {        edges[index] = try model.cloneEdge(allocator, edge);        edge_count += 1;    }    return .{ .files = files, .displaced_edges = edges };}pub fn prepare(    allocator: std.mem.Allocator,    graph: *graph_mod.Graph,    root: []const u8,    paths: []const []const u8,    replace: bool,    options: core.Options,    stats: *core.Stats,) !Plan {    const selected = try reconcile.scope.collect(allocator, graph.*, root, paths, options);    return .{        .files = selected.files,        .displaced_edges = try retire(allocator, graph, root, selected, replace, stats),    };}pub fn finish(allocator: std.mem.Allocator, graph: *graph_mod.Graph, plan: Plan, stats: *core.Stats) !void {    for (plan.displaced_edges) |edge| {        const source_exists = graph_mod.getNode(graph, edge.source) != null;        const target_exists = graph_mod.getNode(graph, edge.target) != null;        if (!source_exists and target_exists and hierarchyEdge(edge)) try ensurePackageHierarchy(allocator, graph, edge.target, stats);    }    for (plan.displaced_edges) |edge| {        if (graph_mod.getNode(graph, edge.source) != null and graph_mod.getNode(graph, edge.target) != null) {            _ = try graph_mod.addEdgeTracked(graph, edge);            continue;        }        if (!manualEdge(edge)) continue;        stats.edges_removed += 1;        try stats.orphaned_manual_edges.append(allocator, .{            .source = edge.source,            .rel = edge.rel,            .target = edge.target,            .reason = if (graph_mod.getNode(graph, edge.source) == null) "source node removed" else "target node removed",        });    }}fn retire(    allocator: std.mem.Allocator,    graph: *graph_mod.Graph,    root: []const u8,    selected: reconcile.scope.Scope,    replace: bool,    stats: *core.Stats,) ![]const model.Edge {    var live = std.StringHashMap(void).init(allocator);    defer live.deinit();    for (selected.files) |file| try live.put(file, {});    var exact = std.StringHashMap(void).init(allocator);    defer exact.deinit();    for (selected.exact) |file| try exact.put(file, {});    var existence = std.StringHashMap(bool).init(allocator);    defer existence.deinit();    var removed = std.StringHashMap(void).init(allocator);    defer removed.deinit();    var packages = std.StringHashMap(void).init(allocator);    defer packages.deinit();    for (graph.nodes.items) |node| {        if (!graph_mod.isScan(node.metadata)) continue;        const file = node.file orelse continue;        if (!try shouldRetireFile(allocator, root, selected, replace, file, live, exact, &existence)) continue;        try removed.put(node.name, {});        try addSyntheticPackageAncestors(allocator, graph.*, node.name, &packages);    }    try addEmptySyntheticPackages(graph.*, &removed, packages);    if (removed.count() == 0) return &.{};    var displaced_edges: std.ArrayList(model.Edge) = .empty;    for (graph.edges.items) |edge| {        if (!removed.contains(edge.source) and !removed.contains(edge.target)) continue;        if (manualEdge(edge) or !removed.contains(edge.source) or (!removed.contains(edge.target) and hierarchyEdge(edge))) try displaced_edges.append(allocator, edge);    }    var names: std.ArrayList([]const u8) = .empty;    for (graph.nodes.items) |node| if (removed.contains(node.name)) try names.append(allocator, node.name);    try graph_mod.removeNodes(graph, names.items);    stats.nodes_removed += names.items.len;    return try displaced_edges.toOwnedSlice(allocator);}fn shouldRetireFile(    allocator: std.mem.Allocator,    root: []const u8,    selected: reconcile.scope.Scope,    replace: bool,    file: []const u8,    live: std.StringHashMap(void),    exact: std.StringHashMap(void),    existence: *std.StringHashMap(bool),) !bool {    const selected_tree = scan_root.scan_files.underPrefixes(selected.trees, file);    if (replace and (live.contains(file) or selected_tree)) return true;    if (exact.contains(file)) return true;    if (!selected_tree) return false;    const entry = try existence.getOrPut(file);    if (!entry.found_existing) {        entry.value_ptr.* = exists: {            const stat = sys.fs.statFile(try std.fs.path.join(allocator, &.{ root, file })) catch |err| switch (err) {                error.FileNotFound, error.NotDir => break :exists false,                else => return err,            };            break :exists stat.kind == .file;        };    }    return !entry.value_ptr.*;}fn manualEdge(edge: model.Edge) bool {    return std.mem.eql(u8, model.pairValue(edge.metadata, "source") orelse "", "manual");}fn hierarchyEdge(edge: model.Edge) bool {    return std.mem.eql(u8, edge.rel, model.RelType.contains) and graph_mod.isScan(edge.metadata);}pub fn ensurePackageHierarchy(allocator: std.mem.Allocator, graph: *graph_mod.Graph, module: []const u8, stats: *core.Stats) !void {    var parts: std.ArrayList([]const u8) = .empty;    var split = std.mem.splitScalar(u8, module, '.');    while (split.next()) |part| try parts.append(allocator, part);    if (parts.items.len < 2) return;    var parent: ?[]const u8 = null;    var index: usize = 0;    while (index + 1 < parts.items.len) : (index += 1) {        const current = try core.joinDotted(allocator, parts.items[0 .. index + 1]);        if (graph_mod.getNode(graph, current) == null) {            const meta = try model.sourcePair(allocator, "scan");            try graph_mod.addNode(graph, .{ .name = current, .type = model.NodeType.package, .metadata = meta });        }        if (parent) |parent_name| {            const meta = try model.sourcePair(allocator, "scan");            try core.addScanEdge(graph, stats, .{ .source = parent_name, .rel = model.RelType.contains, .target = current, .metadata = meta });        }        parent = current;    }    if (parent) |parent_name| {        const meta = try model.sourcePair(allocator, "scan");        try core.addScanEdge(graph, stats, .{ .source = parent_name, .rel = model.RelType.contains, .target = module, .metadata = meta });    }}fn addSyntheticPackageAncestors(allocator: std.mem.Allocator, graph: graph_mod.Graph, name: []const u8, packages: *std.StringHashMap(void)) !void {    for (graph_mod.incomingEdges(&graph, name)) |edge_index| {        const edge = graph.edges.items[edge_index];        if (!std.mem.eql(u8, edge.rel, model.RelType.contains)) continue;        const parent = graph_mod.getNode(&graph, edge.source) orelse continue;        if (!std.mem.eql(u8, parent.type, model.NodeType.package) or parent.file != null or !graph_mod.isScan(parent.metadata)) continue;        const entry = try packages.getOrPut(parent.name);        if (entry.found_existing) continue;        try addSyntheticPackageAncestors(allocator, graph, parent.name, packages);    }}fn addEmptySyntheticPackages(graph: graph_mod.Graph, removed: *std.StringHashMap(void), packages: std.StringHashMap(void)) !void {    var remaining = std.StringHashMap(usize).init(graph.allocator);    defer remaining.deinit();    var queue: std.ArrayList([]const u8) = .empty;    defer queue.deinit(graph.allocator);    var iterator = packages.iterator();    while (iterator.next()) |entry| {        const name = entry.key_ptr.*;        var count: usize = 0;        for (graph_mod.outgoingEdges(&graph, name)) |edge_index| {            const edge = graph.edges.items[edge_index];            if (std.mem.eql(u8, edge.rel, model.RelType.contains) and !removed.contains(edge.target)) count += 1;        }        try remaining.put(name, count);        if (count == 0) try queue.append(graph.allocator, name);    }    var index: usize = 0;    while (index < queue.items.len) : (index += 1) {        const name = queue.items[index];        if (removed.contains(name)) continue;        try removed.put(name, {});        for (graph_mod.incomingEdges(&graph, name)) |edge_index| {            const edge = graph.edges.items[edge_index];            if (!std.mem.eql(u8, edge.rel, model.RelType.contains)) continue;            const count = remaining.getPtr(edge.source) orelse continue;            if (count.* == 0) continue;            count.* -= 1;            if (count.* == 0) try queue.append(graph.allocator, edge.source);        }    }}fn checkClonePlanAllocationFailures(allocator: std.mem.Allocator) !void {    const metadata = [_]model.Pair{.{ .key = "source", .value = "manual" }};    const edges = [_]model.Edge{.{        .source = "manual",        .rel = model.RelType.depends_on,        .target = "app.main",        .metadata = &metadata,    }};    const cloned = try clonePlan(allocator, .{        .files = &.{ "app.py", "lib.py" },        .displaced_edges = &edges,    });    defer {        for (cloned.files) |file| allocator.free(file);        allocator.free(cloned.files);        for (@constCast(cloned.displaced_edges)) |*edge| model.deinitEdge(edge, allocator);        allocator.free(cloned.displaced_edges);    }    try std.testing.expectEqualStrings("app.py", cloned.files[0]);    try std.testing.expectEqualStrings("manual", cloned.displaced_edges[0].source);}test "reconciliation plan clone cleans every allocation failure" {    try std.testing.checkAllAllocationFailures(        std.testing.allocator,        checkClonePlanAllocationFailures,        .{},    );}

Source: tools/smg/src/scan/reconcile/root.zig:2

zig
pub const projection = @import("projection.zig");

Audit

Definitions6
Public names11
Members2
Version26.7.0
Revisiondaab053ee433