lib/choir/src/composition/module/variant.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const choir = @import("../../root.zig");
3 const composition = @import("../root.zig");
4 const callsite = @import("callsite.zig");
5
6 const Allocator = std.mem.Allocator;
7 const product = choir.product;
8 const source = composition.source;
9
10 pub const CompositionVariantId = struct {
11 value: u64,
12 };
13
14 pub const TargetChoiceSpec = struct {
15 target: product.ProductRef,
16 policy: product.ProductRef,
17
18 pub fn eql(self: TargetChoiceSpec, other: TargetChoiceSpec) bool {
19 return self.target.eql(other.target) and self.policy.eql(other.policy);
20 }
21 };
22
23 pub const TargetChoice = struct {
24 target: product.ProductRef,
25 policy: product.ProductRef,
26
27 pub fn init(allocator: Allocator, choice_spec: TargetChoiceSpec) Allocator.Error!TargetChoice {
28 const target = try product.cloneProductRef(allocator, choice_spec.target);
29 errdefer product.deinitProductRef(allocator, target);
30 return .{
31 .target = target,
32 .policy = try product.cloneProductRef(allocator, choice_spec.policy),
33 };
34 }
35
36 pub fn deinit(self: *TargetChoice, allocator: Allocator) void {
37 product.deinitProductRef(allocator, self.policy);
38 product.deinitProductRef(allocator, self.target);
39 self.* = undefined;
40 }
41
42 pub fn eql(self: TargetChoice, choice_spec: TargetChoiceSpec) bool {
43 return self.target.eql(choice_spec.target) and self.policy.eql(choice_spec.policy);
44 }
45
46 pub fn asSpec(self: TargetChoice) TargetChoiceSpec {
47 return .{
48 .target = self.target,
49 .policy = self.policy,
50 };
51 }
52 };
53
54 pub const CompositionVariantSpec = struct {
55 id: CompositionVariantId,
56 choice: TargetChoiceSpec,
57 fragments: []const source.FragmentId,
58 call_sites: []const callsite.CallSiteSpec,
59 };
60
61 /// A complete physical-program choice for one target and policy identity.
62 /// Fragment choices cannot vary independently after this variant is selected.
63 pub const CompositionVariant = struct {
64 id: CompositionVariantId,
65 choice: TargetChoice,
66 fragments: []const source.FragmentId,
67 call_sites: []callsite.CallSite,
68
69 pub fn init(allocator: Allocator, spec: CompositionVariantSpec) Allocator.Error!CompositionVariant {
70 var choice = try TargetChoice.init(allocator, spec.choice);
71 errdefer choice.deinit(allocator);
72 const fragments = try allocator.dupe(source.FragmentId, spec.fragments);
73 errdefer allocator.free(fragments);
74 const calls = try allocator.alloc(callsite.CallSite, spec.call_sites.len);
75 var calls_initialized: usize = 0;
76 errdefer {
77 var index = calls_initialized;
78 while (index != 0) {
79 index -= 1;
80 calls[index].deinit(allocator);
81 }
82 allocator.free(calls);
83 }
84 for (spec.call_sites, calls) |call_spec, *call_value| {
85 call_value.* = try callsite.CallSite.init(allocator, call_spec);
86 calls_initialized += 1;
87 }
88 return .{
89 .id = spec.id,
90 .choice = choice,
91 .fragments = fragments,
92 .call_sites = calls,
93 };
94 }
95
96 pub fn deinit(self: *CompositionVariant, allocator: Allocator) void {
97 var call_index = self.call_sites.len;
98 while (call_index != 0) {
99 call_index -= 1;
100 self.call_sites[call_index].deinit(allocator);
101 }
102 allocator.free(self.call_sites);
103 allocator.free(self.fragments);
104 self.choice.deinit(allocator);
105 self.* = undefined;
106 }
107
108 pub fn containsFragment(self: *const CompositionVariant, id: source.FragmentId) bool {
109 for (self.fragments) |fragment_id| {
110 if (fragment_id.value == id.value) return true;
111 }
112 return false;
113 }
114
115 pub fn callSite(self: *const CompositionVariant, id: source.CallSiteId) ?*const callsite.CallSite {
116 for (self.call_sites) |*call_site| {
117 if (call_site.id.value == id.value) return call_site;
118 }
119 return null;
120 }
121 };