Skip to documentation
SLOP

tiny.smg.concepts

Reference tiny.smg concepts

Defined in tiny.smg.

API (19)

Actions

Public operations.

Types and contracts

Public types and contracts.

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

Source

Called byCallsNo direct callersprivate; no linktools.smg.src.conceptsappendUniqueconceptsadd
Static calls · unresolved targets: 2 · external targets: 2.
Called byCallsprivate; no linktools.smg.src.command.concepts.commandsyncPointprivate; no linktools.smg.src.conceptsappendUniqueconceptsaddSyncPoint
Static calls · unresolved targets: 2 · external targets: 0.
Called byCallsprivate; no linktools.smg.src.command.analyze.commandconceptAnalysistest; no linktools.smg.src.conceptstest: analyze concepts matches python...private sourcelib.pretty.core.src.position.Summaryappendprivate; no linktools.smg.src.conceptsallowedSyncprivate; no linktools.smg.src.conceptscandidateSourcesprivate; no linktools.smg.src.conceptscandidateTargetsprivate; no linktools.smg.src.conceptsconceptByName+12 moreconceptsanalyze
Static calls · unresolved targets: 6 · external targets: 0.
Called byCallsNo direct callsprivate; no linktools.smg.src.command.concepts.commandaddtest; no linktools.smg.src.conceptstest: stale concept publication prese...conceptscreate
Static calls · unresolved targets: 2 · external targets: 2.
Called byCallsNo direct callsprivate; no linktools.smg.src.command.concepts.commandsyncPointconceptshasSyncPoint
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsprivate; no linktools.smg.src.command.concepts.commandlisttest; no linktools.smg.src.conceptstest: concept declarations persist th...test; no linktools.smg.src.conceptstest: concept publication sorts by na...test; no linktools.smg.src.conceptstest: stale concept publication prese...conceptsloadSnapshotconceptsload
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsprivate; no linktools.smg.src.command.check.refreshloadSourcecommand.sessionloadGraphAndConceptsconceptsloadSnapshotprivate; no linktools.smg.src.conceptsstringArrayprivate; no linktools.smg.src.conceptsstringFieldstorage.database.readreaderConceptDocumentsconceptsloadFromReader
Static calls · unresolved targets: 3 · external targets: 3.
Called byCallsprivate; no linktools.smg.src.command.concepts.commandaddprivate; no linktools.smg.src.command.concepts.commandremoveprivate; no linktools.smg.src.command.concepts.commandsyncPointconceptsloadtest; no linktools.smg.src.conceptstest: concept declarations persist th...+2 moreconceptsloadFromReaderstorage.storeopenReadconceptsloadSnapshot
Static calls · unresolved targets: 1 · external targets: 0.
Called byCallsprivate; no linktools.smg.src.command.concepts.commandaddprivate; no linktools.smg.src.command.concepts.commandremoveprivate; no linktools.smg.src.command.concepts.commandsyncPointtest; no linktools.smg.src.conceptstest: concept declarations persist th...test; no linktools.smg.src.conceptstest: concept publication sorts by na...test; no linktools.smg.src.conceptstest: stale concept publication prese...conceptsrendersearchsyncStoredFromDatabasestorage.database.editsapplystorage.database.readdatabaseConceptDocumentsstorage.storebeginPublishstorage.summarywriteForHeadconceptspublish
Static calls · unresolved targets: 1 · external targets: 10.
Called byCallsNo direct callsprivate; no linktools.smg.src.command.concepts.commandremoveconceptsremove
Static calls · unresolved targets: 2 · external targets: 0.
Called byCallsconceptspublishtest; no linktools.smg.src.conceptstest: concept json omits empty sync p...private; no linktools.smg.src.conceptswriteStringArrayconceptsrender
Static calls · unresolved targets: 1 · external targets: 6.
Called byCallstest; no linktools.smg.src.conceptstest: analyze concepts matches python...private; no linktools.smg.src.conceptswriteFloatprivate; no linktools.smg.src.conceptswriteRelCountsprivate; no linktools.smg.src.conceptswriteStringArrayprivate; no linktools.smg.src.conceptswriteWitnessesconceptswriteAnalysisJson
Static calls · unresolved targets: 1 · external targets: 5.

Source: tools/smg/src/concepts.zig

zig
const std = @import("std");const pretty = @import("pretty");const sql = @import("sql");const graph_mod = @import("graph.zig");const limits_mod = @import("limits/root.zig");const search = @import("search.zig");const view = @import("view.zig");const model = @import("model.zig");const storage = @import("storage/root.zig");const pretty_json = pretty.json;const testing_limits = @import("root.zig").default_limits;pub const Concept = struct {    name: []const u8,    prefixes: []const []const u8 = &.{},    sync_points: []const []const u8 = &.{},};pub const Snapshot = struct {    concepts: []const Concept,    head: sql.Hash,};pub const Summary = struct {    name: []const u8,    prefixes: []const []const u8,    anchors: []const []const u8,    members: usize,    internal_edges: usize,    cross_in: usize,    cross_out: usize,    sync_fan_out: usize,    sync_density: f64,    sync_asymmetry: f64,};pub const RelCount = struct {    rel: []const u8,    count: usize,};pub const Dependency = struct {    source: []const u8,    target: []const u8,    edge_count: usize,    rels: []const RelCount,    unsanctioned_count: usize,    unsanctioned_rels: []const RelCount,    witnesses: []const model.Edge,    allowed_sync: bool,};pub const Violation = struct {    source: []const u8,    target: []const u8,    message: []const u8,    rels: []const RelCount,    source_candidates: []const []const u8,    target_candidates: []const []const u8,    witnesses: []const model.Edge,};pub const Analysis = struct {    declared: []const Summary,    dependencies: []const Dependency,    violations: []const Violation,};const Materialized = struct {    concept: Concept,    anchors: []const []const u8,    members: []const []const u8,};const Owner = struct {    member: []const u8,    concept: []const u8,};const Counter = struct {    name: []const u8,    count: usize,};const PairEdges = struct {    source: []const u8,    target: []const u8,    edges: std.ArrayList(model.Edge) = .empty,    unsanctioned: std.ArrayList(model.Edge) = .empty,};const MaterializeResult = struct {    materialized: []const Materialized,    owners: []const Owner,};pub fn analyze(allocator: std.mem.Allocator, graph: graph_mod.Graph, concept_list: []const Concept, overlap_message: ?*[]const u8) !Analysis {    const materialized_result = try materialize(allocator, graph, concept_list, overlap_message);    const materialized = materialized_result.materialized;    const owners = materialized_result.owners;    var internal_edges: std.ArrayList(Counter) = .empty;    var cross_in: std.ArrayList(Counter) = .empty;    var cross_out: std.ArrayList(Counter) = .empty;    var touched_edges: std.ArrayList(Counter) = .empty;    var pairs: std.ArrayList(PairEdges) = .empty;    for (try view.allEdges(graph, allocator)) |edge| {        if (std.mem.eql(u8, edge.rel, model.RelType.contains)) continue;        const source_owner = ownerFor(owners, edge.source);        const target_owner = ownerFor(owners, edge.target);        if (source_owner) |owner| try incrementCounter(allocator, &touched_edges, owner);        if (target_owner) |owner| {            if (source_owner == null or !std.mem.eql(u8, source_owner.?, owner)) try incrementCounter(allocator, &touched_edges, owner);        }        if (source_owner == null or target_owner == null) continue;        if (std.mem.eql(u8, source_owner.?, target_owner.?)) {            try incrementCounter(allocator, &internal_edges, source_owner.?);            continue;        }        try incrementCounter(allocator, &cross_out, source_owner.?);        try incrementCounter(allocator, &cross_in, target_owner.?);        const pair_index = try pairIndex(allocator, &pairs, source_owner.?, target_owner.?);        try pairs.items[pair_index].edges.append(allocator, edge);        const source_concept = conceptByName(materialized, source_owner.?).?;        const target_concept = conceptByName(materialized, target_owner.?).?;        if (!allowedSync(edge, source_concept.concept, target_concept.concept)) try pairs.items[pair_index].unsanctioned.append(allocator, edge);    }    std.mem.sort(PairEdges, pairs.items, {}, pairLess);    var declared: std.ArrayList(Summary) = .empty;    for (materialized) |item| {        const in_count = counterValue(cross_in.items, item.concept.name);        const out_count = counterValue(cross_out.items, item.concept.name);        const touched = counterValue(touched_edges.items, item.concept.name);        try declared.append(allocator, .{            .name = item.concept.name,            .prefixes = item.concept.prefixes,            .anchors = item.anchors,            .members = item.members.len,            .internal_edges = counterValue(internal_edges.items, item.concept.name),            .cross_in = in_count,            .cross_out = out_count,            .sync_fan_out = try syncFanOut(allocator, pairs.items, item.concept.name),            .sync_density = if (touched == 0) 0.0 else roundFloat(@as(f64, @floatFromInt(in_count + out_count)) / @as(f64, @floatFromInt(touched)), 6),            .sync_asymmetry = roundFloat(try syncAsymmetry(allocator, pairs.items, item.concept.name), 6),        });    }    var dependencies: std.ArrayList(Dependency) = .empty;    var violations: std.ArrayList(Violation) = .empty;    for (pairs.items) |pair| {        const edges = try edgeSlice(allocator, pair.edges.items);        const unsanctioned = try edgeSlice(allocator, pair.unsanctioned.items);        const dependency_witnesses = if (unsanctioned.len != 0) try witnessEdges(allocator, unsanctioned) else try witnessEdges(allocator, edges);        try dependencies.append(allocator, .{            .source = pair.source,            .target = pair.target,            .edge_count = edges.len,            .rels = try relCounts(allocator, edges),            .unsanctioned_count = unsanctioned.len,            .unsanctioned_rels = try relCounts(allocator, unsanctioned),            .witnesses = dependency_witnesses,            .allowed_sync = unsanctioned.len == 0,        });        if (unsanctioned.len != 0) {            try violations.append(allocator, .{                .source = pair.source,                .target = pair.target,                .message = try std.fmt.allocPrint(allocator, "{d} unsanctioned cross-concept edge(s)", .{unsanctioned.len}),                .rels = try relCounts(allocator, unsanctioned),                .source_candidates = try candidateSources(allocator, unsanctioned),                .target_candidates = try candidateTargets(allocator, unsanctioned),                .witnesses = try witnessEdges(allocator, unsanctioned),            });        }    }    return .{        .declared = try declared.toOwnedSlice(allocator),        .dependencies = try dependencies.toOwnedSlice(allocator),        .violations = try violations.toOwnedSlice(allocator),    };}pub fn writeAnalysisJson(allocator: std.mem.Allocator, writer: *pretty_json.Writer, result: Analysis) !void {    try writer.beginObject();    try writer.objectField("declared");    try writer.beginArray();    for (result.declared) |summary| {        try writer.beginObject();        try writer.objectField("name");        try writer.write(summary.name);        try writer.objectField("prefixes");        try writeStringArray(writer, summary.prefixes);        try writer.objectField("anchors");        try writeStringArray(writer, summary.anchors);        try writer.objectField("members");        try writer.write(summary.members);        try writer.objectField("internal_edges");        try writer.write(summary.internal_edges);        try writer.objectField("cross_in");        try writer.write(summary.cross_in);        try writer.objectField("cross_out");        try writer.write(summary.cross_out);        try writer.objectField("sync_fan_out");        try writer.write(summary.sync_fan_out);        try writer.objectField("sync_density");        try writeFloat(allocator, writer, summary.sync_density);        try writer.objectField("sync_asymmetry");        try writeFloat(allocator, writer, summary.sync_asymmetry);        try writer.endObject();    }    try writer.endArray();    try writer.objectField("dependencies");    try writer.beginArray();    for (result.dependencies) |dependency| {        try writer.beginObject();        try writer.objectField("source");        try writer.write(dependency.source);        try writer.objectField("target");        try writer.write(dependency.target);        try writer.objectField("edge_count");        try writer.write(dependency.edge_count);        try writer.objectField("rels");        try writeRelCounts(writer, dependency.rels);        try writer.objectField("unsanctioned_count");        try writer.write(dependency.unsanctioned_count);        try writer.objectField("unsanctioned_rels");        try writeRelCounts(writer, dependency.unsanctioned_rels);        try writer.objectField("witnesses");        try writeWitnesses(writer, dependency.witnesses);        try writer.objectField("allowed_sync");        try writer.write(dependency.allowed_sync);        try writer.endObject();    }    try writer.endArray();    try writer.objectField("violations");    try writer.beginArray();    for (result.violations) |violation| {        try writer.beginObject();        try writer.objectField("source");        try writer.write(violation.source);        try writer.objectField("target");        try writer.write(violation.target);        try writer.objectField("message");        try writer.write(violation.message);        try writer.objectField("rels");        try writeRelCounts(writer, violation.rels);        try writer.objectField("sync_candidates");        try writer.beginObject();        try writer.objectField("source");        try writeStringArray(writer, violation.source_candidates);        try writer.objectField("target");        try writeStringArray(writer, violation.target_candidates);        try writer.endObject();        try writer.objectField("sync_commands");        try writer.beginObject();        try writer.objectField("source");        try writer.beginArray();        for (violation.source_candidates) |candidate| {            try writer.write(try std.fmt.allocPrint(allocator, "smg concept sync-point {s} {s}", .{ violation.source, candidate }));        }        try writer.endArray();        try writer.objectField("target");        try writer.beginArray();        for (violation.target_candidates) |candidate| {            try writer.write(try std.fmt.allocPrint(allocator, "smg concept sync-point {s} {s}", .{ violation.target, candidate }));        }        try writer.endArray();        try writer.endObject();        try writer.objectField("witnesses");        try writeWitnesses(writer, violation.witnesses);        try writer.endObject();    }    try writer.endArray();    try writer.endObject();}fn materialize(allocator: std.mem.Allocator, graph: graph_mod.Graph, concept_list: []const Concept, overlap_message: ?*[]const u8) !MaterializeResult {    const sorted = try allocator.dupe(Concept, concept_list);    std.mem.sort(Concept, sorted, {}, conceptLess);    var package_or_module: std.ArrayList([]const u8) = .empty;    var all_names: std.ArrayList([]const u8) = .empty;    for (try view.allNodes(graph, allocator, null)) |node| {        try all_names.append(allocator, node.name);        if (std.mem.eql(u8, node.type, model.NodeType.package) or std.mem.eql(u8, node.type, model.NodeType.module)) try package_or_module.append(allocator, node.name);    }    var out: std.ArrayList(Materialized) = .empty;    var owners: std.ArrayList(Owner) = .empty;    for (sorted) |concept| {        const anchors = try resolveAnchors(allocator, package_or_module.items, concept.prefixes);        var members: std.ArrayList([]const u8) = .empty;        for (all_names.items) |name| {            for (anchors) |anchor| {                if (matchesPrefix(name, anchor)) {                    try appendUniqueList(allocator, &members, name);                    break;                }            }        }        std.mem.sort([]const u8, members.items, {}, stringLess);        for (members.items) |member| {            if (ownerFor(owners.items, member)) |owner| {                if (!std.mem.eql(u8, owner, concept.name)) {                    if (overlap_message) |message| message.* = try std.fmt.allocPrint(allocator, "concept overlap on '{s}': '{s}' and '{s}' both claim it", .{ member, owner, concept.name });                    return error.ConceptOverlap;                }            }            try owners.append(allocator, .{ .member = member, .concept = concept.name });        }        try out.append(allocator, .{ .concept = concept, .anchors = anchors, .members = try members.toOwnedSlice(allocator) });    }    return .{ .materialized = try out.toOwnedSlice(allocator), .owners = try owners.toOwnedSlice(allocator) };}fn resolveAnchors(allocator: std.mem.Allocator, package_or_module: []const []const u8, prefixes: []const []const u8) ![]const []const u8 {    var matches: std.ArrayList([]const u8) = .empty;    for (prefixes) |prefix| {        for (package_or_module) |name| {            if (matchesPrefix(name, prefix)) try appendUniqueList(allocator, &matches, name);        }    }    std.mem.sort([]const u8, matches.items, {}, anchorLess);    var anchors: std.ArrayList([]const u8) = .empty;    for (matches.items) |name| {        var covered = false;        for (anchors.items) |anchor| {            if (matchesPrefix(name, anchor)) {                covered = true;                break;            }        }        if (!covered) try anchors.append(allocator, name);    }    return try anchors.toOwnedSlice(allocator);}fn conceptByName(materialized: []const Materialized, name: []const u8) ?Materialized {    for (materialized) |item| if (std.mem.eql(u8, item.concept.name, name)) return item;    return null;}fn ownerFor(owners: []const Owner, member: []const u8) ?[]const u8 {    for (owners) |owner| if (std.mem.eql(u8, owner.member, member)) return owner.concept;    return null;}fn incrementCounter(allocator: std.mem.Allocator, counters: *std.ArrayList(Counter), name: []const u8) !void {    for (counters.items) |*counter| {        if (std.mem.eql(u8, counter.name, name)) {            counter.count += 1;            return;        }    }    try counters.append(allocator, .{ .name = name, .count = 1 });}fn counterValue(counters: []const Counter, name: []const u8) usize {    for (counters) |counter| if (std.mem.eql(u8, counter.name, name)) return counter.count;    return 0;}fn pairIndex(allocator: std.mem.Allocator, pairs: *std.ArrayList(PairEdges), source: []const u8, target: []const u8) !usize {    for (pairs.items, 0..) |pair, index| {        if (std.mem.eql(u8, pair.source, source) and std.mem.eql(u8, pair.target, target)) return index;    }    try pairs.append(allocator, .{ .source = source, .target = target });    return pairs.items.len - 1;}fn allowedSync(edge: model.Edge, source: Concept, target: Concept) bool {    return matchesAnyPrefix(edge.source, source.sync_points) or matchesAnyPrefix(edge.target, target.sync_points);}fn matchesAnyPrefix(name: []const u8, prefixes: []const []const u8) bool {    for (prefixes) |prefix| if (matchesPrefix(name, prefix)) return true;    return false;}fn syncFanOut(allocator: std.mem.Allocator, pairs: []const PairEdges, name: []const u8) !usize {    var targets: std.ArrayList([]const u8) = .empty;    for (pairs) |pair| {        if (std.mem.eql(u8, pair.source, name)) try appendUniqueList(allocator, &targets, pair.target);    }    return targets.items.len;}fn syncAsymmetry(allocator: std.mem.Allocator, pairs: []const PairEdges, name: []const u8) !f64 {    var related: std.ArrayList([]const u8) = .empty;    for (pairs) |pair| {        if (std.mem.eql(u8, pair.source, name)) try appendUniqueList(allocator, &related, pair.target);        if (std.mem.eql(u8, pair.target, name)) try appendUniqueList(allocator, &related, pair.source);    }    if (related.items.len == 0) return 0.0;    var bidirectional: usize = 0;    var unidirectional: usize = 0;    for (related.items) |other| {        const outgoing = hasPair(pairs, name, other);        const incoming = hasPair(pairs, other, name);        if (outgoing and incoming) {            bidirectional += 1;        } else {            unidirectional += 1;        }    }    return @as(f64, @floatFromInt(unidirectional)) / @as(f64, @floatFromInt(@max(@as(usize, 1), bidirectional)));}fn hasPair(pairs: []const PairEdges, source: []const u8, target: []const u8) bool {    for (pairs) |pair| if (std.mem.eql(u8, pair.source, source) and std.mem.eql(u8, pair.target, target)) return true;    return false;}fn edgeSlice(allocator: std.mem.Allocator, edges: []const model.Edge) ![]const model.Edge {    const out = try allocator.dupe(model.Edge, edges);    std.mem.sort(model.Edge, out, {}, edgeLess);    return out;}fn witnessEdges(allocator: std.mem.Allocator, edges: []const model.Edge) ![]const model.Edge {    const count = @min(edges.len, 5);    return try allocator.dupe(model.Edge, edges[0..count]);}fn relCounts(allocator: std.mem.Allocator, edges: []const model.Edge) ![]const RelCount {    var out: std.ArrayList(RelCount) = .empty;    for (edges) |edge| {        var found = false;        for (out.items) |*item| {            if (std.mem.eql(u8, item.rel, edge.rel)) {                item.count += 1;                found = true;                break;            }        }        if (!found) try out.append(allocator, .{ .rel = edge.rel, .count = 1 });    }    std.mem.sort(RelCount, out.items, {}, relCountLess);    return try out.toOwnedSlice(allocator);}fn candidateSources(allocator: std.mem.Allocator, edges: []const model.Edge) ![]const []const u8 {    var out: std.ArrayList([]const u8) = .empty;    for (edges) |edge| try appendUniqueList(allocator, &out, edge.source);    return try limitStrings(allocator, out.items, 5);}fn candidateTargets(allocator: std.mem.Allocator, edges: []const model.Edge) ![]const []const u8 {    var out: std.ArrayList([]const u8) = .empty;    for (edges) |edge| try appendUniqueList(allocator, &out, edge.target);    return try limitStrings(allocator, out.items, 5);}fn limitStrings(allocator: std.mem.Allocator, values: []const []const u8, limit: usize) ![]const []const u8 {    return try allocator.dupe([]const u8, values[0..@min(values.len, limit)]);}fn writeStringArray(writer: *pretty_json.Writer, values: []const []const u8) !void {    try writer.beginArray();    for (values) |value| try writer.write(value);    try writer.endArray();}fn writeRelCounts(writer: *pretty_json.Writer, counts: []const RelCount) !void {    try writer.beginObject();    for (counts) |count| {        try writer.objectField(count.rel);        try writer.write(count.count);    }    try writer.endObject();}fn writeWitnesses(writer: *pretty_json.Writer, edges: []const model.Edge) !void {    try writer.beginArray();    for (edges) |edge| {        try writer.beginObject();        try writer.objectField("kind");        try writer.write("edge");        try writer.objectField("edges");        try writer.beginArray();        try writeEdgeBrief(writer, edge);        try writer.endArray();        try writer.endObject();    }    try writer.endArray();}fn writeEdgeBrief(writer: *pretty_json.Writer, edge: model.Edge) !void {    try writer.beginObject();    try writer.objectField("source");    try writer.write(edge.source);    try writer.objectField("rel");    try writer.write(edge.rel);    try writer.objectField("target");    try writer.write(edge.target);    try writer.endObject();}fn writeFloat(allocator: std.mem.Allocator, writer: *pretty_json.Writer, value: f64) !void {    const raw = try std.fmt.allocPrint(allocator, "{d:.6}", .{value});    var end = raw.len;    while (end > 0 and raw[end - 1] == '0') end -= 1;    if (end > 0 and raw[end - 1] == '.') end += 1;    try writer.raw(raw[0..end]);}fn roundFloat(value: f64, decimals: usize) f64 {    var scale: f64 = 1.0;    for (0..decimals) |_| scale *= 10.0;    return @round(value * scale) / scale;}fn matchesPrefix(name: []const u8, prefix: []const u8) bool {    return std.mem.eql(u8, name, prefix) or (name.len > prefix.len and std.mem.startsWith(u8, name, prefix) and name[prefix.len] == '.');}fn countDots(name: []const u8) usize {    var count: usize = 0;    for (name) |byte| {        if (byte == '.') count += 1;    }    return count;}fn conceptLess(_: void, a: Concept, b: Concept) bool {    return std.mem.lessThan(u8, a.name, b.name);}fn anchorLess(_: void, a: []const u8, b: []const u8) bool {    const dots_a = countDots(a);    const dots_b = countDots(b);    if (dots_a != dots_b) return dots_a < dots_b;    return std.mem.lessThan(u8, a, b);}fn pairLess(_: void, a: PairEdges, b: PairEdges) bool {    const source = std.mem.order(u8, a.source, b.source);    if (source != .eq) return source == .lt;    return std.mem.lessThan(u8, a.target, b.target);}fn edgeLess(_: void, a: model.Edge, b: model.Edge) bool {    const source = std.mem.order(u8, a.source, b.source);    if (source != .eq) return source == .lt;    const rel = std.mem.order(u8, a.rel, b.rel);    if (rel != .eq) return rel == .lt;    return std.mem.lessThan(u8, a.target, b.target);}fn relCountLess(_: void, a: RelCount, b: RelCount) bool {    return std.mem.lessThan(u8, a.rel, b.rel);}fn stringLess(_: void, a: []const u8, b: []const u8) bool {    return std.mem.lessThan(u8, a, b);}pub fn load(    allocator: std.mem.Allocator,    root: []const u8,    limits: limits_mod.Storage,) ![]const Concept {    return (try loadSnapshot(allocator, root, limits)).concepts;}pub fn loadSnapshot(    allocator: std.mem.Allocator,    root: []const u8,    limits: limits_mod.Storage,) !Snapshot {    var opened = try storage.store.openRead(allocator, root, limits);    defer opened.close();    return .{        .concepts = try loadFromReader(allocator, &opened.reader),        .head = opened.reader.head,    };}pub fn loadFromReader(allocator: std.mem.Allocator, reader: *storage.database.Reader) ![]const Concept {    const documents = try storage.database.read.readerConceptDocuments(reader, allocator, allocator);    defer storage.rows.freeConceptDocumentRowSlice(allocator, allocator, documents);    var out: std.ArrayList(Concept) = .empty;    for (documents) |document| {        var parsed = std.json.parseFromSlice(std.json.Value, allocator, document.document, .{}) catch |err| switch (err) {            error.OutOfMemory => return error.OutOfMemory,            else => continue,        };        defer parsed.deinit();        const object = switch (parsed.value) {            .object => |object| object,            else => continue,        };        const name = stringField(object, "name") orelse continue;        try out.append(allocator, .{            .name = try allocator.dupe(u8, name),            .prefixes = try stringArray(allocator, object.get("prefixes")),            .sync_points = try stringArray(allocator, object.get("sync_points")),        });    }    return try out.toOwnedSlice(allocator);}pub fn publish(    allocator: std.mem.Allocator,    root: []const u8,    concepts: []const Concept,    expected_head: sql.Hash,    limits: limits_mod.Limits,) !void {    const sorted = try allocator.dupe(Concept, concepts);    defer allocator.free(sorted);    std.mem.sort(Concept, sorted, {}, conceptLess);    const puts = try allocator.alloc(storage.rows.ConceptDocumentPut, sorted.len);    defer allocator.free(puts);    var rendered: std.ArrayList([]const u8) = .empty;    defer {        for (rendered.items) |document| allocator.free(document);        rendered.deinit(allocator);    }    try rendered.ensureTotalCapacityPrecise(allocator, sorted.len);    for (sorted, 0..) |concept, index| {        const document = try render(allocator, concept);        rendered.appendAssumeCapacity(document);        puts[index] = .{            .rowid = @intCast(index + 1),            .name = concept.name,            .document = document,        };    }    var publisher = try storage.store.beginPublish(        allocator,        root,        expected_head,        limits.storage,    );    defer publisher.close();    const existing = try storage.database.read.databaseConceptDocuments(&publisher.store.database, allocator, allocator);    defer storage.rows.freeConceptDocumentRowSlice(allocator, allocator, existing);    const deletes = try allocator.alloc(i64, existing.len);    defer allocator.free(deletes);    for (existing, 0..) |document, index| deletes[index] = document.rowid;    try storage.database.edits.apply(&publisher.store.database, .{        .concept_document_puts = puts,        .concept_document_deletes = deletes,    });    try storage.database.commitIfDirty(&publisher.store.database);    const head = (try publisher.store.database.connection.checkout()).head;    try storage.summary.writeForHead(allocator, root, &publisher.store.database, head);    _ = try search.syncStoredFromDatabase(        allocator,        root,        &publisher.store.database,        expected_head,        head,        &.{},        &.{},        limits.search,    );    try publisher.finish();}pub fn add(allocator: std.mem.Allocator, concepts: []const Concept, name: []const u8, prefix: []const u8) ![]const Concept {    var out: std.ArrayList(Concept) = .empty;    var found = false;    for (concepts) |concept| {        if (std.mem.eql(u8, concept.name, name)) {            found = true;            try out.append(allocator, .{                .name = concept.name,                .prefixes = try appendUnique(allocator, concept.prefixes, prefix),                .sync_points = concept.sync_points,            });        } else {            try out.append(allocator, concept);        }    }    if (!found) {        const prefixes = try allocator.alloc([]const u8, 1);        prefixes[0] = try allocator.dupe(u8, prefix);        try out.append(allocator, .{ .name = try allocator.dupe(u8, name), .prefixes = prefixes });    }    return try out.toOwnedSlice(allocator);}pub fn create(allocator: std.mem.Allocator, concepts: []const Concept, name: []const u8, prefixes: []const []const u8, sync_points: []const []const u8) ![]const Concept {    for (concepts) |concept| if (std.mem.eql(u8, concept.name, name)) return error.DuplicateConcept;    var out: std.ArrayList(Concept) = .empty;    for (concepts) |concept| try out.append(allocator, concept);    const owned_prefixes = try allocator.alloc([]const u8, prefixes.len);    for (prefixes, 0..) |prefix, index| owned_prefixes[index] = try allocator.dupe(u8, prefix);    const owned_points = try allocator.alloc([]const u8, sync_points.len);    for (sync_points, 0..) |point, index| owned_points[index] = try allocator.dupe(u8, point);    try out.append(allocator, .{ .name = try allocator.dupe(u8, name), .prefixes = owned_prefixes, .sync_points = owned_points });    return try out.toOwnedSlice(allocator);}pub fn hasSyncPoint(concepts: []const Concept, name: []const u8, point: []const u8) bool {    for (concepts) |concept| {        if (!std.mem.eql(u8, concept.name, name)) continue;        for (concept.sync_points) |existing| if (std.mem.eql(u8, existing, point)) return true;        return false;    }    return false;}pub fn addSyncPoint(allocator: std.mem.Allocator, concepts: []const Concept, name: []const u8, point: []const u8) ![]const Concept {    var out: std.ArrayList(Concept) = .empty;    var found = false;    for (concepts) |concept| {        if (std.mem.eql(u8, concept.name, name)) {            found = true;            try out.append(allocator, .{                .name = concept.name,                .prefixes = concept.prefixes,                .sync_points = try appendUnique(allocator, concept.sync_points, point),            });        } else {            try out.append(allocator, concept);        }    }    if (!found) return error.NotFound;    return try out.toOwnedSlice(allocator);}pub fn remove(allocator: std.mem.Allocator, concepts: []const Concept, name: []const u8) ![]const Concept {    var out: std.ArrayList(Concept) = .empty;    var found = false;    for (concepts) |concept| {        if (std.mem.eql(u8, concept.name, name)) {            found = true;        } else {            try out.append(allocator, concept);        }    }    if (!found) return error.NotFound;    return try out.toOwnedSlice(allocator);}pub fn render(allocator: std.mem.Allocator, concept: Concept) ![]const u8 {    var out: std.Io.Writer.Allocating = .init(allocator);    errdefer out.deinit();    var writer = pretty_json.Writer.init(&out.writer, .minified);    try writer.beginObject();    try writer.objectField("kind");    try writer.write("concept");    try writer.objectField("name");    try writer.write(concept.name);    try writer.objectField("prefixes");    try writeStringArray(&writer, concept.prefixes);    if (concept.sync_points.len != 0) {        try writer.objectField("sync_points");        try writeStringArray(&writer, concept.sync_points);    }    try writer.endObject();    return try out.toOwnedSlice();}fn stringField(object: std.json.ObjectMap, key: []const u8) ?[]const u8 {    const value = object.get(key) orelse return null;    return switch (value) {        .string => |string| string,        else => null,    };}fn stringArray(allocator: std.mem.Allocator, maybe: ?std.json.Value) ![]const []const u8 {    const value = maybe orelse return &.{};    const array = switch (value) {        .array => |array| array,        else => return &.{},    };    var out: std.ArrayList([]const u8) = .empty;    for (array.items) |item| {        if (item == .string) try out.append(allocator, try allocator.dupe(u8, item.string));    }    return try out.toOwnedSlice(allocator);}test "concept declarations persist through the smg SQL store" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var tmp = std.testing.tmpDir(.{});    defer tmp.cleanup();    const root = try tmp.dir.realPathFileAlloc(std.Options.debug_io, ".", allocator);    _ = try storage.initProject(allocator, root, testing_limits.storage);    const values = [_]Concept{        .{ .name = "runtime", .prefixes = &.{ "app.runtime", "lib.runtime" }, .sync_points = &.{"app.runtime.api"} },        .{ .name = "cli", .prefixes = &.{"app.cli"} },    };    const snapshot = try loadSnapshot(allocator, root, testing_limits.storage);    try publish(allocator, root, &values, snapshot.head, testing_limits);    const loaded = try load(allocator, root, testing_limits.storage);    try std.testing.expectEqual(@as(usize, 2), loaded.len);    try std.testing.expectEqualStrings("cli", loaded[0].name);    try std.testing.expectEqualStrings("runtime", loaded[1].name);    try std.testing.expectEqual(@as(usize, 2), loaded[1].prefixes.len);    try std.testing.expectEqualStrings("app.runtime.api", loaded[1].sync_points[0]);}test "stale concept publication preserves the winner and supports reapply" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var tmp = std.testing.tmpDir(.{});    defer tmp.cleanup();    const root = try tmp.dir.realPathFileAlloc(std.Options.debug_io, ".", allocator);    _ = try storage.initProject(allocator, root, testing_limits.storage);    const source = try loadSnapshot(allocator, root, testing_limits.storage);    const winner = try create(allocator, source.concepts, "winner", &.{"app.winner"}, &.{});    const loser = try create(allocator, source.concepts, "loser", &.{"app.loser"}, &.{});    try publish(allocator, root, winner, source.head, testing_limits);    const winner_snapshot = try loadSnapshot(allocator, root, testing_limits.storage);    try std.testing.expectError(error.StaleSourceHead, publish(allocator, root, loser, source.head, testing_limits));    try std.testing.expect(!try storage.sync.refreshIncomplete(allocator, root));    const after_conflict = try loadSnapshot(allocator, root, testing_limits.storage);    try std.testing.expect(sql.version.same(winner_snapshot.head, after_conflict.head));    try std.testing.expectEqual(@as(usize, 1), after_conflict.concepts.len);    try std.testing.expectEqualStrings("winner", after_conflict.concepts[0].name);    _ = try storage.summary.load(allocator, root, testing_limits.storage);    _ = try search.searchStored(allocator, root, "winner", null, 10, testing_limits.storage);    const reapplied = try create(allocator, after_conflict.concepts, "loser", &.{"app.loser"}, &.{});    try publish(allocator, root, reapplied, after_conflict.head, testing_limits);    const final = try load(allocator, root, testing_limits.storage);    try std.testing.expectEqual(@as(usize, 2), final.len);    try std.testing.expectEqualStrings("loser", final[0].name);    try std.testing.expectEqualStrings("winner", final[1].name);}fn appendUniqueList(allocator: std.mem.Allocator, list: *std.ArrayList([]const u8), value: []const u8) !void {    for (list.items) |item| if (std.mem.eql(u8, item, value)) return;    try list.append(allocator, value);}fn appendUnique(allocator: std.mem.Allocator, values: []const []const u8, value: []const u8) ![]const []const u8 {    for (values) |item| if (std.mem.eql(u8, item, value)) return values;    var out: std.ArrayList([]const u8) = .empty;    for (values) |item| try out.append(allocator, item);    try out.append(allocator, try allocator.dupe(u8, value));    return try out.toOwnedSlice(allocator);}test "analyze concepts matches python json contract" {    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 = "app", .type = model.NodeType.package });    try graph_mod.addNode(&graph, .{ .name = "app.core", .type = model.NodeType.module });    try graph_mod.addNode(&graph, .{ .name = "app.core.Engine", .type = model.NodeType.class });    try graph_mod.addNode(&graph, .{ .name = "app.core.Engine.run", .type = model.NodeType.method });    try graph_mod.addNode(&graph, .{ .name = "app.utils", .type = model.NodeType.module });    try graph_mod.addNode(&graph, .{ .name = "app.utils.helper", .type = model.NodeType.function });    try graph_mod.addEdge(&graph, .{ .source = "app", .rel = model.RelType.contains, .target = "app.core" });    try graph_mod.addEdge(&graph, .{ .source = "app", .rel = model.RelType.contains, .target = "app.utils" });    try graph_mod.addEdge(&graph, .{ .source = "app.core", .rel = model.RelType.contains, .target = "app.core.Engine" });    try graph_mod.addEdge(&graph, .{ .source = "app.core.Engine", .rel = model.RelType.contains, .target = "app.core.Engine.run" });    try graph_mod.addEdge(&graph, .{ .source = "app.utils", .rel = model.RelType.contains, .target = "app.utils.helper" });    try graph_mod.addEdge(&graph, .{ .source = "app.core", .rel = model.RelType.imports, .target = "app.utils" });    try graph_mod.addEdge(&graph, .{ .source = "app.core.Engine.run", .rel = model.RelType.calls, .target = "app.utils.helper" });    const prefix_core = [_][]const u8{"app.core"};    const prefix_utils = [_][]const u8{"app.utils"};    const concept_list = [_]Concept{        .{ .name = "core", .prefixes = &prefix_core },        .{ .name = "utils", .prefixes = &prefix_utils },    };    const result = try analyze(allocator, graph, &concept_list, null);    var out: std.Io.Writer.Allocating = .init(allocator);    var writer = pretty_json.Writer.init(&out.writer, .minified);    try writeAnalysisJson(allocator, &writer, result);    try out.writer.writeByte('\n');    try std.testing.expectEqualStrings(        \\{"declared":[{"name":"core","prefixes":["app.core"],"anchors":["app.core"],"members":3,"internal_edges":0,"cross_in":0,"cross_out":2,"sync_fan_out":1,"sync_density":1.0,"sync_asymmetry":1.0},{"name":"utils","prefixes":["app.utils"],"anchors":["app.utils"],"members":2,"internal_edges":0,"cross_in":2,"cross_out":0,"sync_fan_out":0,"sync_density":1.0,"sync_asymmetry":1.0}],"dependencies":[{"source":"core","target":"utils","edge_count":2,"rels":{"calls":1,"imports":1},"unsanctioned_count":2,"unsanctioned_rels":{"calls":1,"imports":1},"witnesses":[{"kind":"edge","edges":[{"source":"app.core","rel":"imports","target":"app.utils"}]},{"kind":"edge","edges":[{"source":"app.core.Engine.run","rel":"calls","target":"app.utils.helper"}]}],"allowed_sync":false}],"violations":[{"source":"core","target":"utils","message":"2 unsanctioned cross-concept edge(s)","rels":{"calls":1,"imports":1},"sync_candidates":{"source":["app.core","app.core.Engine.run"],"target":["app.utils","app.utils.helper"]},"sync_commands":{"source":["smg concept sync-point core app.core","smg concept sync-point core app.core.Engine.run"],"target":["smg concept sync-point utils app.utils","smg concept sync-point utils app.utils.helper"]},"witnesses":[{"kind":"edge","edges":[{"source":"app.core","rel":"imports","target":"app.utils"}]},{"kind":"edge","edges":[{"source":"app.core.Engine.run","rel":"calls","target":"app.utils.helper"}]}]}]}        \\    , out.written());}test "concept json omits empty sync points like python schema" {    const rendered = try render(std.testing.allocator, .{ .name = "utils", .prefixes = &.{"app.utils"} });    defer std.testing.allocator.free(rendered);    try std.testing.expectEqualStrings("{\"kind\":\"concept\",\"name\":\"utils\",\"prefixes\":[\"app.utils\"]}", rendered);}test "concept publication sorts by name like python storage" {    var arena = std.heap.ArenaAllocator.init(std.testing.allocator);    defer arena.deinit();    const allocator = arena.allocator();    var tmp = std.testing.tmpDir(.{});    defer tmp.cleanup();    const root = try tmp.dir.realPathFileAlloc(std.Options.debug_io, ".", allocator);    _ = try storage.initProject(allocator, root, testing_limits.storage);    const snapshot = try loadSnapshot(allocator, root, testing_limits.storage);    const list = [_]Concept{        .{ .name = "ui", .prefixes = &.{"app.ui"} },        .{ .name = "data", .prefixes = &.{"app.db"} },    };    try publish(allocator, root, &list, snapshot.head, testing_limits);    const loaded = try load(allocator, root, testing_limits.storage);    try std.testing.expectEqual(@as(usize, 2), loaded.len);    try std.testing.expectEqualStrings("data", loaded[0].name);    try std.testing.expectEqualStrings("ui", loaded[1].name);    try std.testing.expectEqual(@as(usize, 0), (try storage.summary.load(allocator, root, testing_limits.storage)).nodes);    try std.testing.expectEqual(@as(i64, 0), (try search.searchStored(allocator, root, "data", null, 10, testing_limits.storage)).total);}

Source: tools/smg/src/root.zig:18

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

Complete call list for concepts.analyze

17 direct calls.

Complete caller list for concepts.loadSnapshot

7 direct callers.

Audit

Definitions20
Public names20
Members35
Version26.7.0
Revisiondaab053ee433