repositories / termux-janitor
termux-janitor
Interactive cleanup assistant for Termux: transparent, safe, confirmed disk reclamation.
owned by admin
tools/spec-engine/src/query.zig
Rawconst std = @import("std");
const model_types = @import("model.zig");
const diag = @import("diag.zig");
const project = @import("project.zig");
const text = @import("text.zig");
const findObligation = model_types.findObligation;
const findRequirement = model_types.findRequirement;
const findTestBinding = model_types.findTestBinding;
const findTestSpec = model_types.findTestSpec;
/// Answer a generic relationship query.
///
/// Product-domain queries are dispatched by the profile consumer before this
/// function is reached.
pub fn runQuery(
model: *const model_types.Graph,
args: []const []const u8,
writer: *std.Io.Writer,
) !void {
if (args.len < 2 or args.len > 3) return error.InvalidQuery;
const json = args.len == 3 and std.mem.eql(u8, args[2], "--json");
if (args.len == 3 and !json) return error.InvalidQuery;
if (std.mem.eql(u8, args[0], "requirement")) {
return queryRequirement(model, args[1], json, writer);
}
if (std.mem.eql(u8, args[0], "concept")) {
return queryConcept(model, args[1], json, writer);
}
if (std.mem.eql(u8, args[0], "document")) {
return queryDocument(model, args[1], json, writer);
}
if (std.mem.eql(u8, args[0], "owner")) {
return queryOwner(model, args[1], json, writer);
}
if (std.mem.eql(u8, args[0], "test-spec")) {
return queryTestSpec(model, args[1], json, writer);
}
if (std.mem.eql(u8, args[0], "obligation")) {
return queryObligation(model, args[1], json, writer);
}
if (std.mem.eql(u8, args[0], "implementation")) {
return queryImplementation(model, args[1], json, writer);
}
return error.UnknownQuery;
}
pub fn queryRequirement(
model: *const model_types.Graph,
id: []const u8,
json: bool,
writer: *std.Io.Writer,
) !void {
for (model.groups) |group| {
for (group.requirements) |requirement| {
if (!std.mem.eql(u8, requirement.id, id)) continue;
if (json) {
try writer.print("{f}\n", .{std.json.fmt(requirement, .{})});
} else {
try writer.print("{s}@{d}: {s}\n", .{
requirement.id,
requirement.revision,
requirement.label,
});
try writer.print("owner: {s}\n", .{requirement.owner});
try writer.writeAll("suites: ");
try project.renderSuites(requirement.suites, writer);
try writer.print(
"\nlimitation: {s}\n",
.{@tagName(requirement.limitation_status)},
);
}
return;
}
}
return error.RequirementNotFound;
}
pub fn queryConcept(
model: *const model_types.Graph,
key: []const u8,
json: bool,
writer: *std.Io.Writer,
) !void {
for (model.concepts) |concept| {
const matches = std.mem.eql(u8, concept.id, key) or
std.ascii.eqlIgnoreCase(concept.term, key);
if (!matches) continue;
if (json) {
try writer.print("{f}\n", .{std.json.fmt(concept, .{})});
} else {
try writer.print("{s}: {s}\nowner: {s}\ndefinition: ", .{
concept.id,
concept.term,
concept.owner,
});
try text.writeCollapsedWhitespace(writer, concept.definition);
try writer.writeAll("\ninvariants:\n");
for (concept.invariants) |invariant| {
try writer.writeAll("- ");
try text.writeCollapsedWhitespace(writer, invariant);
try writer.writeByte('\n');
}
}
return;
}
return error.ConceptNotFound;
}
pub fn queryDocument(
model: *const model_types.Graph,
name: []const u8,
json: bool,
writer: *std.Io.Writer,
) !void {
for (model.documents) |document| {
if (!std.mem.eql(u8, document.name, name)) continue;
if (json) {
try writer.print("{f}\n", .{std.json.fmt(document, .{})});
} else {
try writer.print("{s}: {s}\n{s}\n", .{
document.name,
document.location,
document.description,
});
}
return;
}
return error.DocumentNotFound;
}
pub fn queryOwner(
model: *const model_types.Graph,
owner: []const u8,
json: bool,
writer: *std.Io.Writer,
) !void {
var match_count: u32 = 0;
if (json) try writer.writeByte('[');
for (model.groups) |group| {
for (group.requirements) |requirement| {
if (!std.mem.eql(u8, requirement.owner, owner)) continue;
if (json) {
if (match_count > 0) try writer.writeByte(',');
try writer.print("{f}", .{std.json.fmt(requirement.id, .{})});
} else {
try writer.print("{s}@{d}: {s}\n", .{
requirement.id,
requirement.revision,
requirement.label,
});
}
match_count += 1;
}
}
if (json) try writer.writeAll("]\n");
if (match_count == 0) return error.OwnerNotFound;
}
pub fn queryTestSpec(
model: *const model_types.Graph,
id: []const u8,
json: bool,
writer: *std.Io.Writer,
) !void {
const test_spec = findTestSpec(model.test_specs, id) orelse return error.TestSpecNotFound;
if (json) {
try writer.print("{f}\n", .{std.json.fmt(test_spec, .{})});
return;
}
try writer.print("{s}@{d}\nsuite: {s}\nfixture: {s}@{d}\npath: {s}\n", .{
test_spec.id,
test_spec.revision,
@tagName(test_spec.suite),
test_spec.fixture.id,
test_spec.fixture.revision,
test_spec.fixture.path,
});
if (findTestBinding(model.test_bindings, test_spec.id)) |binding| {
try writer.print("state: bound\nsource: {s}\n", .{binding.source});
} else {
try writer.writeAll("state: specified\n");
}
try writer.writeAll("claims:\n");
for (test_spec.claims) |claim| {
try writer.print("- {s} ({s})\n", .{ claim.oracle, @tagName(claim.kind) });
for (claim.obligations) |obligation| {
try writer.print(" - {s}@{d}\n", .{ obligation.id, obligation.revision });
}
}
}
pub fn queryObligation(
model: *const model_types.Graph,
id: []const u8,
json: bool,
writer: *std.Io.Writer,
) !void {
const obligation = findObligation(model.verification, id) orelse
return error.ObligationNotFound;
if (json) return writer.print("{f}\n", .{std.json.fmt(obligation, .{})});
try writer.print("{s}@{d}: {s}\nkind: {s}\nrequirement: {s}@{d}\nsuites:", .{
obligation.id,
obligation.revision,
obligation.label,
@tagName(obligation.kind),
obligation.requirement.id,
obligation.requirement.revision,
});
for (obligation.suites) |suite| try writer.print(" {s}", .{@tagName(suite)});
try writer.writeByte('\n');
}
pub fn queryImplementation(
model: *const model_types.Graph,
id: []const u8,
json: bool,
writer: *std.Io.Writer,
) !void {
for (model.implementations) |unit| {
if (!std.mem.eql(u8, unit.id, id)) continue;
if (json) return writer.print("{f}\n", .{std.json.fmt(unit, .{})});
try writer.print("{s}@{d}\nsources:\n", .{ unit.id, unit.revision });
for (unit.sources) |source| try writer.print("- {s}\n", .{source.path});
try writer.writeAll("realizes:\n");
for (unit.realizes) |reference| {
try writer.print("- {s}@{d}\n", .{ reference.id, reference.revision });
}
return;
}
return error.ImplementationNotFound;
}
pub fn runFingerprint(
allocator: std.mem.Allocator,
io: std.Io,
path: []const u8,
writer: *std.Io.Writer,
stderr: *std.Io.Writer,
) !void {
try diag.validateRepositoryPath(io, path, stderr);
const source = try std.Io.Dir.cwd().readFileAlloc(
io,
path,
allocator,
.limited(model_types.file_size_max),
);
defer allocator.free(source);
var digest: [std.crypto.hash.sha2.Sha256.digest_length]u8 = undefined;
std.crypto.hash.sha2.Sha256.hash(source, &digest, .{});
try writer.print("{x} {s}\n", .{ &digest, path });
}
pub fn runImpact(
model: *const model_types.Graph,
target: []const u8,
json: bool,
writer: *std.Io.Writer,
) !void {
if (json) return runImpactJson(model, target, writer);
var matched = false;
for (model.groups) |group| {
for (group.requirements) |requirement| {
if (!std.mem.eql(u8, requirement.id, target)) continue;
matched = true;
try writer.print("requirement {s}@{d}\n owner {s}\n", .{
requirement.id,
requirement.revision,
requirement.owner,
});
}
}
for (model.verification) |obligation| {
if (std.mem.eql(u8, obligation.id, target) or
std.mem.eql(u8, obligation.requirement.id, target))
{
matched = true;
try writer.print("obligation {s}@{d}\n requirement {s}@{d}\n", .{
obligation.id,
obligation.revision,
obligation.requirement.id,
obligation.requirement.revision,
});
}
}
for (model.test_specs) |test_spec| {
if (testSpecMatchesImpact(model, &test_spec, target)) {
matched = true;
try writer.print("test {s}@{d}\n fixture {s}@{d}\n path {s}\n", .{
test_spec.id,
test_spec.revision,
test_spec.fixture.id,
test_spec.fixture.revision,
test_spec.fixture.path,
});
}
}
for (model.implementations) |unit| {
if (implementationMatchesImpact(model, &unit, target)) {
matched = true;
try writer.print("implementation {s}@{d}\n", .{ unit.id, unit.revision });
for (unit.sources) |source| try writer.print(" source {s}\n", .{source.path});
}
}
if (!matched) return error.ImpactTargetNotFound;
}
fn runImpactJson(
model: *const model_types.Graph,
target: []const u8,
writer: *std.Io.Writer,
) !void {
var matched = false;
try writer.print("{{\"target\":{f},\"requirements\":[", .{std.json.fmt(target, .{})});
var count: u32 = 0;
for (model.groups) |group| {
for (group.requirements) |requirement| {
if (!std.mem.eql(u8, requirement.id, target)) continue;
if (count > 0) try writer.writeByte(',');
try writer.print("{f}", .{std.json.fmt(requirement, .{})});
count += 1;
matched = true;
}
}
try writer.writeAll("],\"obligations\":[");
count = 0;
for (model.verification) |obligation| {
if (!std.mem.eql(u8, obligation.id, target) and
!std.mem.eql(u8, obligation.requirement.id, target)) continue;
if (count > 0) try writer.writeByte(',');
try writer.print("{f}", .{std.json.fmt(obligation, .{})});
count += 1;
matched = true;
}
try writer.writeAll("],\"tests\":[");
count = 0;
for (model.test_specs) |test_spec| {
if (!testSpecMatchesImpact(model, &test_spec, target)) continue;
if (count > 0) try writer.writeByte(',');
try writer.print("{f}", .{std.json.fmt(test_spec, .{})});
count += 1;
matched = true;
}
try writer.writeAll("],\"implementations\":[");
count = 0;
for (model.implementations) |unit| {
if (!implementationMatchesImpact(model, &unit, target)) continue;
if (count > 0) try writer.writeByte(',');
try writer.print("{f}", .{std.json.fmt(unit, .{})});
count += 1;
matched = true;
}
try writer.writeAll("]}\n");
if (!matched) return error.ImpactTargetNotFound;
}
pub fn testSpecMatchesImpact(
model: *const model_types.Graph,
test_spec: *const model_types.TestSpec,
target: []const u8,
) bool {
if (std.mem.eql(u8, test_spec.id, target) or
std.mem.eql(u8, test_spec.fixture.id, target) or
std.mem.eql(u8, test_spec.fixture.path, target)) return true;
for (model.test_bindings) |binding| {
if (std.mem.eql(u8, binding.test_spec.id, test_spec.id) and
std.mem.eql(u8, binding.source, target)) return true;
}
for (test_spec.claims) |claim| {
if (std.mem.eql(u8, claim.oracle, target)) return true;
for (claim.obligations) |reference| {
if (std.mem.eql(u8, reference.id, target)) return true;
const obligation = findObligation(model.verification, reference.id) orelse continue;
if (std.mem.eql(u8, obligation.requirement.id, target)) return true;
}
}
return false;
}
pub fn implementationMatchesImpact(
model: *const model_types.Graph,
unit: *const model_types.ImplementationUnit,
target: []const u8,
) bool {
if (std.mem.eql(u8, unit.id, target)) return true;
for (unit.sources) |source| {
if (std.mem.eql(u8, source.path, target)) return true;
}
for (unit.realizes) |reference| {
if (std.mem.eql(u8, reference.id, target)) return true;
const obligation = findObligation(model.verification, reference.id) orelse continue;
if (std.mem.eql(u8, obligation.requirement.id, target)) return true;
}
return false;
}
test "missing queries fail explicitly" {
const model: model_types.Graph = .{
.documents = &.{},
.concepts = &.{},
.groups = &.{},
.test_specs = &.{},
.test_bindings = &.{},
.verification = &.{},
.implementations = &.{},
};
var output = std.Io.Writer.Allocating.init(std.testing.allocator);
defer output.deinit();
try std.testing.expectError(
error.RequirementNotFound,
queryRequirement(&model, "REQ-MISSING-01", false, &output.writer),
);
try std.testing.expectError(
error.ConceptNotFound,
queryConcept(&model, "missing", false, &output.writer),
);
try std.testing.expectError(
error.DocumentNotFound,
queryDocument(&model, "missing", false, &output.writer),
);
try std.testing.expectError(
error.TestSpecNotFound,
queryTestSpec(&model, "missing", false, &output.writer),
);
}