Zig 0.16 · v0.0.2 preview

File architecture rules

File rules are the general-purpose policy surface: choose project files, select a mood, and assert names, locations, dependencies, cycles, or a Zig callback.

A compiled example

The canonical example below covers an internal dependency allowlist and an approved external-module set through the public package facade.

const std = @import("std");
const archunit = @import("archunit");

test "API files depend on service files" {
    var files = try archunit.files(std.testing.allocator, .{});
    defer files.deinit();
    var api = try files.inPath(&.{.{ .glob = "src/api/**" }});
    defer api.deinit();
    var should = try api.should();
    defer should.deinit();
    var dependency = try should.dependOnFiles();
    defer dependency.deinit();
    var rule = try dependency.inPath(&.{.{ .glob = "src/service/**" }});
    defer rule.deinit(std.testing.allocator);
    try archunit.expectPasses(&rule, .init(.init(std.testing.allocator, std.testing.io)));
}

Compiled source: test/readme/files.zig

Select subject files

Start with archunit.files or archunit.projectFiles. Narrow the owned scope using withName, inFolder, inPath, and exact inFile selectors. Pattern alternatives inside one call are OR; separate calls compose as AND. Add except immediately after a selector to keep generated or exceptional paths outside the subject.

Shipped predicates

PredicateUse it for
haveNameFilename conventions.
beInFolder / beInPathLocation and ownership conventions.
dependOnFilesInternal dependency requirements or prohibitions.
dependOnExternalModulesPackage, compiler-module, C-header, or resource policy.
haveNoCyclesDeterministic directed-cycle detection over the selected graph.
adhereToA caller-defined Zig predicate with structured custom violations.

Dependency objects use the same grammar

After dependOnFiles, select the dependency object with the same filename, folder, path, exact-file, and exclusion vocabulary. Internal self-edges retain isolated nodes but do not count as real dependencies. A violation owns the source, target, import kinds, and source locations.

External dependency scope

Named packages are selected by default. Opt into compiler modules, C headers, or resources when the policy needs them. Resolved project aliases are internal; explicitly package-origin aliases and unresolved aliases remain external. Negated rules use the same classified facts.

Cycle results

Cycle detection works over selected internal paths and returns canonical closed paths, so the first node also appears at the end. Ordering is deterministic, and structured results keep edge evidence rather than reducing a cycle to prose.

Custom policy callbacks

adhereTo receives borrowed file information and a caller-provided type-erased context. It may return a policy result or propagate a callback error. Custom violations own their description and affected path, so formatting remains stable after callback state is gone.

Prefer a structural rule when one fits Built-in predicates preserve richer dependency evidence and can evolve with the shared diagnostics contract. Use callbacks for genuinely project-specific semantics.
Next: Layers