tiny.smg.command.session
Defined in command.
API (10)
Actions
Public operations.
loadloadGraphAndConceptsloadMutationloadMutationAtloadReadloadReadGraphAndConceptsnoteGraphAge
Types and contracts
Public types and contracts.
Source
Source: tools/smg/src/command/root.zig:21
zig
pub const session = @import("session.zig");Source: tools/smg/src/command/session.zig
zig
const std = @import("std");const alloc = @import("alloc");const sql = @import("sql");const sys = @import("sys");const smg = @import("../root.zig");const concepts = smg.concepts;const graph_mod = smg.graph;const model = smg.model;const output = smg.cli.output;const search = smg.search;const storage = smg.storage;pub const PhaseBoundary = alloc.PhaseBoundary;pub fn load( allocator: std.mem.Allocator, scratch: std.mem.Allocator, limits: smg.Limits,) !struct { []const u8, graph_mod.Graph } { const loaded = try loadMutation(allocator, scratch, limits); return .{ loaded.root, loaded.graph };}pub const GraphMutation = struct { root: []const u8, graph: graph_mod.Graph, head: sql.Hash,};pub fn loadMutation( allocator: std.mem.Allocator, scratch: std.mem.Allocator, limits: smg.Limits,) !GraphMutation { const root = try storage.requireRoot(allocator); return try loadMutationAt(allocator, scratch, root, limits);}pub fn loadMutationAt( allocator: std.mem.Allocator, scratch: std.mem.Allocator, root: []const u8, limits: smg.Limits,) !GraphMutation { try noteGraphAge(allocator, root); const snapshot = try storage.graph.loadSnapshot( allocator, scratch, root, limits.storage, ); return .{ .root = root, .graph = snapshot.graph, .head = snapshot.head };}pub fn loadRead( allocator: std.mem.Allocator, scratch: std.mem.Allocator, limits: smg.Limits, boundary: PhaseBoundary,) !struct { []const u8, graph_mod.Graph } { const loaded = try load(allocator, scratch, limits); boundary.seal(); return loaded;}pub const GraphConcepts = struct { root: []const u8, graph: graph_mod.Graph, concepts: []const concepts.Concept,};pub fn loadGraphAndConcepts( allocator: std.mem.Allocator, scratch: std.mem.Allocator, limits: smg.Limits,) !GraphConcepts { const root = try storage.requireRoot(allocator); try noteGraphAge(allocator, root); var opened = try storage.store.openRead(scratch, root, limits.storage); defer opened.close(); var graph = try storage.graph.loadFromReader(allocator, scratch, &opened.reader); errdefer graph_mod.deinit(&graph); return .{ .root = root, .graph = graph, .concepts = try concepts.loadFromReader(allocator, &opened.reader), };}pub fn loadReadGraphAndConcepts( allocator: std.mem.Allocator, scratch: std.mem.Allocator, limits: smg.Limits, boundary: PhaseBoundary,) !GraphConcepts { const loaded = try loadGraphAndConcepts(allocator, scratch, limits); boundary.seal(); return loaded;}const graph_age_note_ns: i128 = 60 * 60 * std.time.ns_per_s;pub fn noteGraphAge(allocator: std.mem.Allocator, root: []const u8) !void { var newest: ?i128 = null; for ([_][]const u8{ storage.paths.wal_file_name, storage.paths.database_file_name }) |file_name| { const path = try std.fs.path.join(allocator, &.{ root, storage.paths.smg_dir_name, file_name }); defer allocator.free(path); const stat = sys.fs.statFile(path) catch continue; if (newest == null or stat.mtime.nanoseconds > newest.?) newest = stat.mtime.nanoseconds; } const mtime = newest orelse return; const age_ns = sys.time.realNanoTimestamp() - mtime; if (age_ns < graph_age_note_ns) return; const age_text = try humanAge(allocator, age_ns); defer allocator.free(age_text); try output.writeErrFmt(allocator, "note: graph last scanned {s} ago; run smg scan to refresh\n", .{age_text});}fn humanAge(allocator: std.mem.Allocator, age_ns: i128) ![]u8 { const seconds: i64 = @intCast(@divTrunc(age_ns, std.time.ns_per_s)); if (seconds >= 2 * 24 * 60 * 60) return try std.fmt.allocPrint(allocator, "{d}d", .{@divTrunc(seconds, 24 * 60 * 60)}); if (seconds >= 60 * 60) return try std.fmt.allocPrint(allocator, "{d}h", .{@divTrunc(seconds, 60 * 60)}); if (seconds >= 60) return try std.fmt.allocPrint(allocator, "{d}m", .{@divTrunc(seconds, 60)}); return try std.fmt.allocPrint(allocator, "{d}s", .{@max(seconds, 0)});}test "humanAge reports days hours and minutes" { const day = try humanAge(std.testing.allocator, 3 * 24 * 60 * 60 * std.time.ns_per_s); defer std.testing.allocator.free(day); try std.testing.expectEqualStrings("3d", day); const hour = try humanAge(std.testing.allocator, 5 * 60 * 60 * std.time.ns_per_s); defer std.testing.allocator.free(hour); try std.testing.expectEqualStrings("5h", hour); const minute = try humanAge(std.testing.allocator, 7 * 60 * std.time.ns_per_s); defer std.testing.allocator.free(minute); try std.testing.expectEqualStrings("7m", minute);}test "graph publication releases persistence scratch and preserves search row order" { var arena = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena.deinit(); const allocator = arena.allocator(); const root = try std.fmt.allocPrint(allocator, "/tmp/smg-main-search-rowids-{x}", .{@as(u64, @intCast(@max(0, sys.time.realMilliTimestamp())))}); defer sys.fs.deleteTree(root) catch {}; try sys.fs.createDirPath(root); _ = try storage.initProject(allocator, root, smg.default_limits.storage); const snapshot = try storage.graph.loadSnapshot( allocator, allocator, root, smg.default_limits.storage, ); var graph = graph_mod.init(allocator); try graph_mod.addNode(&graph, .{ .name = "smg.cli.helpers._truncate", .type = model.NodeType.function, .file = "src/smg/cli/_helpers.py", .line = 42, .docstring = "Trim a string to width with an ellipsis" }); try graph_mod.addNode(&graph, .{ .name = "smg.graph.SemGraph", .type = model.NodeType.class, .file = "src/smg/graph.py", .line = 10, .docstring = "Core semantic graph data structure" }); try graph_mod.addNode(&graph, .{ .name = "smg.graph.SemGraph.truncate_path", .type = model.NodeType.method, .file = "src/smg/graph.py", .line = 412, .docstring = "Drop path components after max depth" }); try graph_mod.addNode(&graph, .{ .name = "smg.scan.scan_paths", .type = model.NodeType.function, .file = "src/smg/scan.py", .line = 100, .docstring = "Scan source files and populate graph" }); try graph_mod.addNode(&graph, .{ .name = "smg.cli.helpers.format_output", .type = model.NodeType.function, .file = "src/smg/cli/_helpers.py", .line = 80, .docstring = "Format output for display" }); try storage.graph.publish( std.testing.allocator, root, graph, snapshot.head, smg.default_limits, ); const result = try testSearchStored(allocator, root, "truncate", null, 10); try std.testing.expectEqual(@as(i64, 2), result.total); try std.testing.expectEqual(@as(usize, 2), result.hits.len); try std.testing.expectEqualStrings("smg.cli.helpers._truncate", result.hits[0].name); try std.testing.expectEqualStrings("smg.graph.SemGraph.truncate_path", result.hits[1].name); const near_path = try testSearchStored(allocator, root, "NEAR(truncate path, 5)", null, 10); try std.testing.expectEqual(@as(i64, 1), near_path.total); try std.testing.expectEqualStrings("smg.graph.SemGraph.truncate_path", near_path.hits[0].name); const near_helpers = try testSearchStored(allocator, root, "NEAR(truncate helpers, 5)", null, 10); try std.testing.expectEqual(@as(i64, 1), near_helpers.total); try std.testing.expectEqualStrings("smg.cli.helpers._truncate", near_helpers.hits[0].name); const bare_near = try testSearchStored(allocator, root, "NEAR", null, 10); try std.testing.expectEqual(@as(i64, 0), bare_near.total); try std.testing.expectError(search.Error.InvalidSearchQuery, testSearchStored(allocator, root, "NEAR(truncate path, bad)", null, 10)); const cross_field_phrase = try testSearchStored(allocator, root, "\"path Drop\"", null, 10); try std.testing.expectEqual(@as(i64, 0), cross_field_phrase.total); const cross_field_near = try testSearchStored(allocator, root, "NEAR(truncate max, 10)", null, 10); try std.testing.expectEqual(@as(i64, 0), cross_field_near.total); const cross_field_and = try testSearchStored(allocator, root, "truncate AND max", null, 10); try std.testing.expectEqual(@as(i64, 1), cross_field_and.total); try std.testing.expectEqualStrings("smg.graph.SemGraph.truncate_path", cross_field_and.hits[0].name); const name_filter = try testSearchStored(allocator, root, "name_tokens:truncate", null, 10); try std.testing.expectEqual(@as(i64, 2), name_filter.total); try std.testing.expectEqualStrings("smg.cli.helpers._truncate", name_filter.hits[0].name); try std.testing.expectEqualStrings("smg.graph.SemGraph.truncate_path", name_filter.hits[1].name); const doc_filter = try testSearchStored(allocator, root, "docstring:max", null, 10); try std.testing.expectEqual(@as(i64, 1), doc_filter.total); try std.testing.expectEqualStrings("smg.graph.SemGraph.truncate_path", doc_filter.hits[0].name); const left_group = try testSearchStored(allocator, root, "(truncate OR output) AND format", null, 10); try std.testing.expectEqual(@as(i64, 1), left_group.total); try std.testing.expectEqualStrings("smg.cli.helpers.format_output", left_group.hits[0].name); const right_group = try testSearchStored(allocator, root, "truncate AND (path OR helpers)", null, 10); try std.testing.expectEqual(@as(i64, 2), right_group.total); try std.testing.expectEqualStrings("smg.graph.SemGraph.truncate_path", right_group.hits[0].name); try std.testing.expectEqualStrings("smg.cli.helpers._truncate", right_group.hits[1].name);}fn testSearchStored( allocator: std.mem.Allocator, root: []const u8, query: []const u8, kind: ?[]const u8, limit: i64,) !search.Result { return try search.searchStored( allocator, root, query, kind, limit, smg.default_limits.storage, );}Audit
| Definitions | 11 |
|---|---|
| Public names | 11 |
| Members | 6 |
| Version | 26.7.0 |
| Revision | daab053ee433 |