Skip to content

Commit c5b936f

Browse files
committed
Extract macro property application
1 parent 7d72065 commit c5b936f

3 files changed

Lines changed: 103 additions & 30 deletions

File tree

PLAN.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -569,7 +569,7 @@ Exit criteria:
569569

570570
- M0 is complete as the Zig `0.16.0` runtime scaffold.
571571
- M1 has started on stacked branches with native Zig artifact-first parser slices.
572-
- M1A has started with behavior-preserving `src/project/types.zig`, `src/project/selector.zig`, `src/project/manifest.zig`, `src/project/util.zig`, `src/project/config.zig`, `src/project/fs.zig`, `src/project/jinja.zig`, `src/project/resolve.zig`, and `src/project/parse.zig` extractions. `src/project/manifest.zig` owns selected-resource JSON and partial `manifest.json` writing with native tests for selected JSON shape, JSON escaping, exposure dependency ordering, disabled-resource filtering, and graph-map output. `src/project/util.zig` owns shared display, membership, append-dedup, string sorting, and narrow YAML scalar/list helpers. `src/project/config.zig` owns `dbt_project.yml` loading, project path/docs config parsing, and applying parsed project path/docs configs to graph nodes. `src/project/fs.zig` owns deterministic resource file discovery, Linux directory traversal helpers, and resource path/name helpers. `src/project/jinja.zig` owns lexical Jinja call, parenthesis, quoted string, literal argument helpers, supported model SQL scanning, inline config/tag parsing, and known macro-call scanning with native tests. `src/project/resolve.zig` owns graph lookup/count helpers, canonical graph resource ordering, duplicate resource validation, macro unique-id package extraction, low-level ref/source resolution helpers, and dependency-map mutation for refs, sources, and known macro dependencies. `src/project/parse.zig` owns narrow parser scalar helpers for YAML booleans, JSON-compatible scalar classification, generic-test YAML item names, generic-test definition construction/cloning, generic-test relationship target ref parsing, and generic-test identity/name/hash helpers with native tests. `src/project.zig` remains the public parser/list facade and still owns most loader, resource parser, and resolver orchestration code until follow-up extractions move those pieces behind focused internal modules.
572+
- M1A has started with behavior-preserving `src/project/types.zig`, `src/project/selector.zig`, `src/project/manifest.zig`, `src/project/util.zig`, `src/project/config.zig`, `src/project/fs.zig`, `src/project/jinja.zig`, `src/project/resolve.zig`, and `src/project/parse.zig` extractions. `src/project/manifest.zig` owns selected-resource JSON and partial `manifest.json` writing with native tests for selected JSON shape, JSON escaping, exposure dependency ordering, disabled-resource filtering, and graph-map output. `src/project/util.zig` owns shared display, membership, append-dedup, string sorting, and narrow YAML scalar/list helpers. `src/project/config.zig` owns `dbt_project.yml` loading, project path/docs config parsing, and applying parsed project path/docs configs to graph nodes. `src/project/fs.zig` owns deterministic resource file discovery, Linux directory traversal helpers, and resource path/name helpers. `src/project/jinja.zig` owns lexical Jinja call, parenthesis, quoted string, literal argument helpers, supported model SQL scanning, inline config/tag parsing, and known macro-call scanning with native tests. `src/project/resolve.zig` owns graph lookup/count helpers, canonical graph resource ordering, duplicate resource validation, macro unique-id package extraction, low-level ref/source resolution helpers, and dependency-map mutation for refs, sources, and known macro dependencies. `src/project/parse.zig` owns narrow parser scalar helpers for YAML booleans, JSON-compatible scalar classification, generic-test YAML item names, generic-test definition construction/cloning, generic-test relationship target ref parsing, macro-property application, and generic-test identity/name/hash helpers with native tests. `src/project.zig` remains the public parser/list facade and still owns most loader, resource parser, and resolver orchestration code until follow-up extractions move those pieces behind focused internal modules.
573573
- `dxt parse` now targets the supported Tier 0 subset: project name/model paths/seed paths/macro paths, project and package model path configs for literal `+materialized`, `+tags`, and model/seed `+docs.node_color`, root-project model config overrides for installed packages, SQL model discovery, CSV seed discovery, installed package SQL model and CSV seed discovery from `dbt_packages`, source discovery, installed package source discovery, exposure discovery, installed package exposure discovery, project macro discovery, installed package macro discovery from `dbt_packages`, macro property YAML for project macro descriptions and arguments, project and package docs block discovery, literal `ref` to models or seeds, two-argument package refs, package-local refs in installed package models and exposures, unique installed-package fallback for unqualified refs, literal `source`, package-local sources in installed package models, unique installed-package fallback for unqualified sources, literal `doc` in project and package descriptions, inline `config(materialized=..., tags=...)`, known project/package-qualified/package-local macro call dependencies, narrow project and package YAML model properties for scalar descriptions, simple columns, tags, materialization, disabled SQL models, dbt-shaped `unique`, `not_null`, `accepted_values`, and `relationships` generic test nodes, model/test `refs` and `sources` artifact fields, dependency maps, and deterministic partial `manifest.json`. The manifest includes the v12 top-level maps needed by the M1 artifact shape and is covered by a pinned local dbt Manifest v12 schema slice. YAML generic test arguments are currently supported for scalar values plus inline and block lists required by public Jaffle Shop DuckDB-style tests.
574574
- `dxt ls` now lists dbt-selectable resources from the same parser graph with stable text/JSON output and basic name/FQN wildcards, tag wildcards, slash-aware `path:` wildcards, exact `package:`/`package:this`, `source:` wildcards including package-qualified source selectors, `exposure:` wildcards, `resource_type:`, `test_type:generic`, config materialization, comma intersection, whitespace union, multi-argument selector lists, repeated selector flags, leading/trailing `+` graph expansion, and exact exclude filters; macros are emitted in artifacts but not exposed as `ls` resources.
575575
- Synthetic fixtures cover one model, model refs, seed refs, source refs, exposure refs to models and sources, combined source/model YAML, inline config/tag selection, config materialization selection, comma-intersection selection, YAML model properties and columns, emitted `unique`, `not_null`, `accepted_values`, and `relationships` generic test nodes, project macro artifacts and macro properties, configured `macro-paths` replacing the default macro directory, installed package macros with package-qualified calls and package-local macro calls, installed package models, seeds, sources, docs, exposures, package YAML model properties, root package config overrides, and package-qualified/package-local refs/sources, macro calls recorded in model and macro `depends_on.macros`, docs blocks with literal `doc` descriptions, disabled models, disabled ref diagnostics, unmatched model-property warnings, duplicate model and docs diagnostics, unsupported dynamic ref/doc diagnostics, missing doc diagnostics, malformed docs block diagnostics, unresolved package macro diagnostics, and unsupported unknown macro-call diagnostics.

src/project.zig

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,7 @@ const ColumnDef = types.ColumnDef;
2323
const GenericTestDef = types.GenericTestDef;
2424
const DocBlock = types.DocBlock;
2525
const MacroDef = types.MacroDef;
26-
const MacroArgument = types.MacroArgument;
2726
const ModelProperty = types.ModelProperty;
28-
const MacroProperty = types.MacroProperty;
2927
const Node = types.Node;
3028
const GenericTestNode = types.GenericTestNode;
3129
const Graph = types.Graph;
@@ -49,6 +47,7 @@ const splitKeyValue = util.splitKeyValue;
4947
const parseInlineStringList = util.parseInlineStringList;
5048
const dupTrimmedScalar = util.dupTrimmedScalar;
5149
const sortStrings = util.sortStrings;
50+
const applyMacroProperties = project_parse.applyMacroProperties;
5251
const appendGenericTestDef = project_parse.appendGenericTestDef;
5352
const appendGenericTestDefClone = project_parse.appendGenericTestDefClone;
5453
const parseBool = project_parse.parseBool;
@@ -68,7 +67,6 @@ const countActiveExposures = project_resolve.countActiveExposures;
6867
const countActiveNodes = project_resolve.countActiveNodes;
6968
const countActiveSeeds = project_resolve.countActiveSeeds;
7069
const findDoc = project_resolve.findDoc;
71-
const findMacroIndexByPackageAndName = project_resolve.findMacroIndexByPackageAndName;
7270
const findModelIndexByName = project_resolve.findModelIndexByName;
7371
const rejectDuplicateDocs = project_resolve.rejectDuplicateDocs;
7472
const rejectDuplicateExposures = project_resolve.rejectDuplicateExposures;
@@ -998,21 +996,6 @@ fn parseSeed(runtime: Runtime, seed_root: []const u8, relative_path: []const u8,
998996
try graph.nodes.append(runtime.allocator, node);
999997
}
1000998

1001-
fn applyMacroProperties(graph: *Graph) !void {
1002-
for (graph.macro_properties.items) |property| {
1003-
const macro_index = findMacroIndexByPackageAndName(graph, property.package_name, property.name) orelse {
1004-
try graph.unmatched_macro_properties.append(graph.allocator, .{ .name = property.name, .patch_path = property.patch_path });
1005-
continue;
1006-
};
1007-
var macro = &graph.macros.items[macro_index];
1008-
macro.patch_path = property.patch_path;
1009-
if (property.description.len != 0) macro.description = property.description;
1010-
for (property.arguments.items) |argument| {
1011-
try appendMacroArgumentClone(graph, &macro.arguments, argument);
1012-
}
1013-
}
1014-
}
1015-
1016999
fn applyModelProperties(graph: *Graph, package_name: []const u8) !void {
10171000
for (graph.model_properties.items) |property| {
10181001
if (!std.mem.eql(u8, property.package_name, package_name)) continue;
@@ -1173,17 +1156,6 @@ fn resolveDocDescription(graph: *Graph, package_name: []const u8, description: [
11731156
return doc.block_contents;
11741157
}
11751158

1176-
fn appendMacroArgumentClone(graph: *Graph, arguments: *std.ArrayList(MacroArgument), source: MacroArgument) !void {
1177-
for (arguments.items) |*existing| {
1178-
if (std.mem.eql(u8, existing.name, source.name)) {
1179-
if (source.type.len != 0) existing.type = source.type;
1180-
if (source.description.len != 0) existing.description = source.description;
1181-
return;
1182-
}
1183-
}
1184-
try arguments.append(graph.allocator, source);
1185-
}
1186-
11871159
fn appendMetaEntry(allocator: std.mem.Allocator, entries: *std.ArrayList(MetaEntry), key: []const u8, value: JsonScalar) !void {
11881160
for (entries.items) |*existing| {
11891161
if (std.mem.eql(u8, existing.key, key)) {

src/project/parse.zig

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
const std = @import("std");
22
const jinja = @import("jinja.zig");
3+
const resolve = @import("resolve.zig");
34
const types = @import("types.zig");
45
const util = @import("util.zig");
56

67
const JsonScalar = types.JsonScalar;
78
const GenericTestDef = types.GenericTestDef;
89
const Graph = types.Graph;
10+
const MacroArgument = types.MacroArgument;
911
const RefDep = types.RefDep;
1012
const dupTrimmedScalar = util.dupTrimmedScalar;
1113
const findMatchingParen = jinja.findMatchingParen;
1214
const parseLiteralArgs = jinja.parseLiteralArgs;
15+
const findMacroIndexByPackageAndName = resolve.findMacroIndexByPackageAndName;
1316

1417
pub fn parseBool(value: []const u8) !bool {
1518
const trimmed = std.mem.trim(u8, value, " \t\r");
@@ -101,6 +104,32 @@ pub fn appendGenericTestDefClone(graph: *Graph, tests: *std.ArrayList(GenericTes
101104
try tests.append(graph.allocator, cloned);
102105
}
103106

107+
pub fn applyMacroProperties(graph: *Graph) !void {
108+
for (graph.macro_properties.items) |property| {
109+
const macro_index = findMacroIndexByPackageAndName(graph, property.package_name, property.name) orelse {
110+
try graph.unmatched_macro_properties.append(graph.allocator, .{ .name = property.name, .patch_path = property.patch_path });
111+
continue;
112+
};
113+
var macro = &graph.macros.items[macro_index];
114+
macro.patch_path = property.patch_path;
115+
if (property.description.len != 0) macro.description = property.description;
116+
for (property.arguments.items) |argument| {
117+
try appendMacroArgumentClone(graph, &macro.arguments, argument);
118+
}
119+
}
120+
}
121+
122+
fn appendMacroArgumentClone(graph: *Graph, arguments: *std.ArrayList(MacroArgument), source: MacroArgument) !void {
123+
for (arguments.items) |*existing| {
124+
if (std.mem.eql(u8, existing.name, source.name)) {
125+
if (source.type.len != 0) existing.type = source.type;
126+
if (source.description.len != 0) existing.description = source.description;
127+
return;
128+
}
129+
}
130+
try arguments.append(graph.allocator, source);
131+
}
132+
104133
pub fn refDepFromValue(allocator: std.mem.Allocator, value: []const u8) !RefDep {
105134
const trimmed = std.mem.trim(u8, value, " \t\r");
106135
if (std.mem.startsWith(u8, trimmed, "ref(")) {
@@ -368,6 +397,78 @@ test "appendGenericTestDefClone copies nested accepted values list" {
368397
try std.testing.expectEqualStrings("returned", clones.items[0].accepted_values.items[1]);
369398
}
370399

400+
test "applyMacroProperties applies descriptions patch paths and merges arguments" {
401+
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
402+
defer arena.deinit();
403+
const allocator = arena.allocator();
404+
405+
var graph = Graph{ .allocator = allocator, .project_name = "demo" };
406+
defer graph.deinit();
407+
408+
try graph.macros.append(allocator, .{
409+
.unique_id = "macro.demo.format_id",
410+
.package_name = "demo",
411+
.name = "format_id",
412+
.path = "macros/format_id.sql",
413+
.original_file_path = "macros/format_id.sql",
414+
.macro_sql = "",
415+
});
416+
try graph.macros.items[0].arguments.append(allocator, .{
417+
.name = "column_name",
418+
.type = "string",
419+
.description = "",
420+
});
421+
422+
try graph.macro_properties.append(allocator, .{
423+
.package_name = "demo",
424+
.name = "format_id",
425+
.patch_path = "macros/schema.yml",
426+
.description = "Formats an identifier.",
427+
});
428+
try graph.macro_properties.items[0].arguments.append(allocator, .{
429+
.name = "column_name",
430+
.type = "",
431+
.description = "Identifier expression.",
432+
});
433+
try graph.macro_properties.items[0].arguments.append(allocator, .{
434+
.name = "quote",
435+
.type = "bool",
436+
.description = "Whether to quote.",
437+
});
438+
439+
try applyMacroProperties(&graph);
440+
441+
try std.testing.expectEqualStrings("macros/schema.yml", graph.macros.items[0].patch_path.?);
442+
try std.testing.expectEqualStrings("Formats an identifier.", graph.macros.items[0].description);
443+
try std.testing.expectEqual(@as(usize, 2), graph.macros.items[0].arguments.items.len);
444+
try std.testing.expectEqualStrings("column_name", graph.macros.items[0].arguments.items[0].name);
445+
try std.testing.expectEqualStrings("string", graph.macros.items[0].arguments.items[0].type);
446+
try std.testing.expectEqualStrings("Identifier expression.", graph.macros.items[0].arguments.items[0].description);
447+
try std.testing.expectEqualStrings("quote", graph.macros.items[0].arguments.items[1].name);
448+
try std.testing.expectEqualStrings("bool", graph.macros.items[0].arguments.items[1].type);
449+
}
450+
451+
test "applyMacroProperties records unmatched macro properties" {
452+
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
453+
defer arena.deinit();
454+
const allocator = arena.allocator();
455+
456+
var graph = Graph{ .allocator = allocator, .project_name = "demo" };
457+
defer graph.deinit();
458+
459+
try graph.macro_properties.append(allocator, .{
460+
.package_name = "demo",
461+
.name = "missing_macro",
462+
.patch_path = "macros/schema.yml",
463+
});
464+
465+
try applyMacroProperties(&graph);
466+
467+
try std.testing.expectEqual(@as(usize, 1), graph.unmatched_macro_properties.items.len);
468+
try std.testing.expectEqualStrings("missing_macro", graph.unmatched_macro_properties.items[0].name);
469+
try std.testing.expectEqualStrings("macros/schema.yml", graph.unmatched_macro_properties.items[0].patch_path);
470+
}
471+
371472
test "refDepFromValue parses relationship target refs and raw model names" {
372473
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
373474
defer arena.deinit();

0 commit comments

Comments
 (0)